@perspective-dev/viewer 4.5.2 → 5.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 (227) hide show
  1. package/dist/cdn/perspective-viewer.js +20 -1
  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/extensions.d.ts +22 -3
  24. package/dist/esm/perspective-viewer.d.ts +9 -0
  25. package/dist/esm/perspective-viewer.inline.js +20 -1
  26. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  27. package/dist/esm/perspective-viewer.js +20 -1
  28. package/dist/esm/perspective-viewer.js.map +4 -4
  29. package/dist/esm/plugin.d.ts +71 -5
  30. package/dist/esm/ts-rs/ClientOptions.d.ts +7 -0
  31. package/dist/esm/ts-rs/ExportMethod.d.ts +1 -0
  32. package/dist/esm/ts-rs/ExportOptions.d.ts +9 -0
  33. package/dist/esm/ts-rs/GetClientOptions.d.ts +8 -0
  34. package/dist/esm/ts-rs/GetTableOptions.d.ts +8 -0
  35. package/dist/esm/ts-rs/Layout.d.ts +11 -0
  36. package/dist/esm/ts-rs/Orientation.d.ts +4 -0
  37. package/dist/esm/ts-rs/PanelOptions.d.ts +7 -0
  38. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +41 -0
  39. package/dist/esm/ts-rs/SplitLayout.d.ts +7 -0
  40. package/dist/esm/ts-rs/TabLayout.d.ts +10 -0
  41. package/dist/esm/ts-rs/ViewerConfig.d.ts +8 -2
  42. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +9 -0
  43. package/dist/esm/ts-rs/WindowAggregate.d.ts +1 -0
  44. package/dist/esm/ts-rs/WindowSort.d.ts +7 -0
  45. package/dist/esm/ts-rs/WindowSortDir.d.ts +6 -0
  46. package/dist/esm/ts-rs/WindowSpec.d.ts +46 -0
  47. package/dist/esm/ts-rs/Windows.d.ts +10 -0
  48. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +40 -0
  49. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +28 -0
  50. package/dist/wasm/perspective-viewer.d.ts +299 -78
  51. package/dist/wasm/perspective-viewer.js +544 -186
  52. package/dist/wasm/perspective-viewer.wasm +0 -0
  53. package/dist/wasm/perspective-viewer.wasm.d.ts +34 -26
  54. package/dist/wasm/snippets/perspective-viewer-8a8c7d5d696b43ae/inline0.js +6 -0
  55. package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline3.js → perspective-viewer-8a8c7d5d696b43ae/inline4.js} +5 -0
  56. package/package.json +3 -2
  57. package/src/css/_context_menu.css +14 -0
  58. package/src/css/column-selector.css +1 -0
  59. package/src/css/column-settings-panel.css +68 -3
  60. package/src/css/column-style.css +4 -3
  61. package/src/css/config-selector.css +22 -1
  62. package/src/css/containers/context-menu.css +107 -0
  63. package/src/css/containers/tabs.css +2 -2
  64. package/src/css/panel-tab.css +193 -0
  65. package/src/css/plugin-selector.css +7 -10
  66. package/src/css/status-bar.css +75 -81
  67. package/src/css/viewer.css +188 -70
  68. package/src/rust/components/column_dropdown.rs +40 -30
  69. package/src/rust/components/column_selector/active_column.rs +28 -45
  70. package/src/rust/components/column_selector/aggregate_selector.rs +4 -7
  71. package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
  72. package/src/rust/components/column_selector/config_selector.rs +40 -82
  73. package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
  74. package/src/rust/components/column_selector/filter_column.rs +5 -15
  75. package/src/rust/components/column_selector/inactive_column.rs +11 -9
  76. package/src/rust/components/column_selector/sort_column.rs +3 -6
  77. package/src/rust/components/column_selector.rs +25 -14
  78. package/src/rust/components/column_settings_sidebar/style_tab.rs +2 -1
  79. package/src/rust/components/column_settings_sidebar/window_tab.rs +34 -0
  80. package/src/rust/components/column_settings_sidebar.rs +163 -16
  81. package/src/rust/components/containers/dragdrop_list.rs +73 -3
  82. package/src/rust/components/containers/sidebar.rs +99 -22
  83. package/src/rust/components/containers/split_panel.rs +181 -62
  84. package/src/rust/components/context_menu.rs +130 -0
  85. package/src/rust/components/editable_header.rs +2 -0
  86. package/src/rust/components/form/debug.rs +3 -0
  87. package/src/rust/components/global_filter_bar.rs +76 -0
  88. package/src/rust/components/main_panel/frame_theme.rs +186 -0
  89. package/src/rust/components/main_panel/msg.rs +62 -0
  90. package/src/rust/components/main_panel/presize.rs +220 -0
  91. package/src/rust/components/main_panel/reconcile.rs +224 -0
  92. package/src/rust/components/main_panel/render.rs +385 -0
  93. package/src/rust/components/main_panel/update.rs +184 -0
  94. package/src/rust/components/main_panel.rs +316 -71
  95. package/src/rust/components/mod.rs +5 -0
  96. package/src/rust/components/panel_menu.rs +493 -0
  97. package/src/rust/components/panel_tab.rs +510 -0
  98. package/src/rust/components/plugin_tab.rs +1 -1
  99. package/src/rust/components/portal.rs +7 -0
  100. package/src/rust/components/settings_panel.rs +13 -5
  101. package/src/rust/components/status_bar.rs +110 -178
  102. package/src/rust/components/status_indicator.rs +6 -9
  103. package/src/rust/components/style/mod.rs +5 -0
  104. package/src/rust/components/style/style_provider.rs +2 -0
  105. package/src/rust/components/viewer/filters.rs +133 -0
  106. package/src/rust/components/viewer/msg.rs +182 -0
  107. package/src/rust/components/viewer/panels.rs +326 -0
  108. package/src/rust/components/viewer/render.rs +255 -0
  109. package/src/rust/components/viewer/settings.rs +434 -0
  110. package/src/rust/components/viewer/snapshots.rs +134 -0
  111. package/src/rust/components/viewer/wiring.rs +505 -0
  112. package/src/rust/components/viewer.rs +245 -756
  113. package/src/rust/components/window_editor.rs +1013 -0
  114. package/src/rust/config/export_method.rs +1 -1
  115. package/src/rust/config/mod.rs +4 -0
  116. package/src/rust/config/options.rs +71 -0
  117. package/src/rust/config/viewer_config.rs +24 -2
  118. package/src/rust/config/workspace_config.rs +92 -0
  119. package/src/rust/custom_elements/copy_dropdown.rs +3 -1
  120. package/src/rust/custom_elements/debug_plugin.rs +20 -1
  121. package/src/rust/custom_elements/export_dropdown.rs +3 -1
  122. package/src/rust/custom_elements/viewer.rs +1015 -286
  123. package/src/rust/custom_events.rs +212 -61
  124. package/src/rust/js/mod.rs +2 -0
  125. package/src/rust/js/plugin.rs +297 -10
  126. package/src/rust/js/regular_layout.rs +258 -0
  127. package/src/rust/lib.rs +24 -2
  128. package/src/rust/presentation/column_locator.rs +16 -1
  129. package/src/rust/presentation.rs +66 -33
  130. package/src/rust/queries/columns_iter_set.rs +7 -1
  131. package/src/rust/queries/get_viewer_config.rs +29 -11
  132. package/src/rust/queries/plugin_column_styles.rs +2 -2
  133. package/src/rust/renderer/activate.rs +41 -14
  134. package/src/rust/renderer/dispatch.rs +463 -0
  135. package/src/rust/renderer/plugin_config.rs +495 -0
  136. package/src/rust/renderer/registry.rs +18 -6
  137. package/src/rust/renderer/render_timer.rs +29 -10
  138. package/src/rust/renderer.rs +384 -838
  139. package/src/rust/session/column_defaults_update.rs +26 -8
  140. package/src/rust/session/drag_drop_update.rs +18 -0
  141. package/src/rust/session/metadata.rs +66 -9
  142. package/src/rust/session/replace_expression_update.rs +1 -0
  143. package/src/rust/session/view_subscription.rs +30 -15
  144. package/src/rust/session.rs +755 -264
  145. package/src/rust/tasks/apply_global_filters.rs +89 -0
  146. package/src/rust/tasks/auto_pause.rs +171 -0
  147. package/src/rust/tasks/copy_export.rs +29 -19
  148. package/src/rust/tasks/create_panel.rs +243 -0
  149. package/src/rust/tasks/dismiss_render_warning.rs +3 -1
  150. package/src/rust/tasks/edit_expression.rs +4 -4
  151. package/src/rust/tasks/edit_window.rs +155 -0
  152. package/src/rust/tasks/eject.rs +64 -23
  153. package/src/rust/tasks/mod.rs +30 -4
  154. package/src/rust/tasks/pipeline.rs +411 -0
  155. package/src/rust/tasks/presize_panels.rs +324 -0
  156. package/src/rust/tasks/reset_all.rs +22 -12
  157. package/src/rust/tasks/resize_observer.rs +124 -87
  158. package/src/rust/tasks/restore_and_render.rs +80 -86
  159. package/src/rust/tasks/restore_panel.rs +145 -0
  160. package/src/rust/tasks/send_column_config.rs +5 -2
  161. package/src/rust/tasks/send_plugin_config.rs +5 -2
  162. package/src/rust/tasks/set_edit_mode.rs +59 -0
  163. package/src/rust/tasks/sync_update_panels.rs +152 -0
  164. package/src/rust/tasks/table_lifecycle.rs +158 -0
  165. package/src/rust/tasks/update_theme.rs +56 -12
  166. package/src/rust/utils/browser/dragdrop.rs +12 -0
  167. package/src/rust/utils/browser/tests/debounce.rs +49 -0
  168. package/src/rust/utils/completion.rs +56 -0
  169. package/src/rust/utils/custom_element.rs +5 -0
  170. package/src/rust/utils/debounce.rs +189 -17
  171. package/src/rust/utils/mod.rs +6 -0
  172. package/src/rust/utils/pending_effects.rs +62 -0
  173. package/src/rust/utils/spawn.rs +33 -0
  174. package/src/rust/utils/weak_scope.rs +0 -10
  175. package/src/rust/workspace.rs +846 -0
  176. package/src/svg/active-indicator.svg +3 -0
  177. package/src/svg/pin-icon.svg +5 -0
  178. package/src/themes/botanical.css +5 -2
  179. package/src/themes/defaults.css +54 -2
  180. package/src/themes/dracula.css +6 -3
  181. package/src/themes/gruvbox-dark.css +5 -2
  182. package/src/themes/gruvbox.css +5 -2
  183. package/src/themes/icons.css +5 -2
  184. package/src/themes/intl/de.css +7 -0
  185. package/src/themes/intl/es.css +7 -0
  186. package/src/themes/intl/fr.css +7 -0
  187. package/src/themes/intl/ja.css +7 -0
  188. package/src/themes/intl/pt.css +7 -0
  189. package/src/themes/intl/zh.css +7 -0
  190. package/src/themes/intl.css +10 -1
  191. package/src/themes/monokai.css +5 -2
  192. package/src/themes/phosphor.css +5 -2
  193. package/src/themes/pro-dark.css +5 -2
  194. package/src/themes/pro.css +2 -1
  195. package/src/themes/solarized-dark.css +5 -2
  196. package/src/themes/solarized.css +5 -2
  197. package/src/themes/vaporwave.css +5 -2
  198. package/src/ts/bootstrap.ts +8 -7
  199. package/src/ts/extensions.ts +30 -2
  200. package/src/ts/perspective-viewer.ts +10 -0
  201. package/src/ts/plugin.ts +72 -5
  202. package/src/ts/ts-rs/ClientOptions.ts +7 -0
  203. package/src/ts/ts-rs/ExportMethod.ts +3 -0
  204. package/src/ts/ts-rs/ExportOptions.ts +8 -0
  205. package/src/ts/ts-rs/GetClientOptions.ts +7 -0
  206. package/src/ts/ts-rs/GetTableOptions.ts +7 -0
  207. package/src/ts/ts-rs/Layout.ts +9 -0
  208. package/src/ts/ts-rs/Orientation.ts +6 -0
  209. package/src/ts/ts-rs/PanelOptions.ts +7 -0
  210. package/src/ts/ts-rs/PanelViewerConfig.ts +16 -0
  211. package/src/ts/ts-rs/SplitLayout.ts +5 -0
  212. package/src/ts/ts-rs/TabLayout.ts +11 -0
  213. package/src/ts/ts-rs/ViewerConfig.ts +7 -2
  214. package/src/ts/ts-rs/ViewerConfigUpdate.ts +9 -0
  215. package/src/ts/ts-rs/WindowAggregate.ts +3 -0
  216. package/src/ts/ts-rs/WindowSort.ts +9 -0
  217. package/src/ts/ts-rs/WindowSortDir.ts +8 -0
  218. package/src/ts/ts-rs/WindowSpec.ts +46 -0
  219. package/src/ts/ts-rs/Windows.ts +10 -0
  220. package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
  221. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
  222. package/src/rust/tasks/intersection_observer.rs +0 -98
  223. package/src/rust/tasks/update_and_render.rs +0 -110
  224. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline0.js → perspective-viewer-8a8c7d5d696b43ae/inline1.js} +0 -0
  225. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline1.js → perspective-viewer-8a8c7d5d696b43ae/inline2.js} +0 -0
  226. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline2.js → perspective-viewer-8a8c7d5d696b43ae/inline3.js} +0 -0
  227. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline4.js → perspective-viewer-8a8c7d5d696b43ae/inline5.js} +0 -0
@@ -13,21 +13,21 @@
13
13
  use std::collections::HashSet;
14
14
 
15
15
  use perspective_client::config::*;
16
- use perspective_js::utils::ApiFuture;
17
16
  use web_sys::*;
18
17
  use yew::prelude::*;
19
18
 
20
19
  use super::InPlaceColumn;
21
20
  use super::aggregate_selector::*;
21
+ use super::column_selector_column_row::ColumnSelectorColumnRow;
22
22
  use super::expr_edit_button::*;
23
23
  use crate::components::column_dropdown::ColumnDropDownElement;
24
24
  use crate::components::column_selector::{EmptyColumn, InvalidColumn};
25
- use crate::components::type_icon::TypeIcon;
26
25
  use crate::config::ColumnSelectMode;
27
26
  use crate::presentation::{ColumnLocator, Presentation};
28
27
  use crate::queries::*;
29
28
  use crate::renderer::*;
30
29
  use crate::session::*;
30
+ use crate::tasks::apply_and_render;
31
31
  use crate::utils::*;
32
32
 
33
33
  #[derive(Clone, Properties)]
@@ -66,6 +66,10 @@ pub struct ActiveColumnProps {
66
66
  #[prop_or_default]
67
67
  pub is_expression: bool,
68
68
 
69
+ /// Whether this column is a window column.
70
+ #[prop_or_default]
71
+ pub is_window: bool,
72
+
69
73
  /// Whether the expression/config edit button should be shown. Computed
70
74
  /// by the parent (`is_expression || can_render_column_styles`).
71
75
  #[prop_or_default]
@@ -95,6 +99,7 @@ impl PartialEq for ActiveColumnProps {
95
99
  && self.is_aggregated == rhs.is_aggregated
96
100
  && self.is_editing == rhs.is_editing
97
101
  && self.is_expression == rhs.is_expression
102
+ && self.is_window == rhs.is_window
98
103
  && self.show_edit_btn == rhs.show_edit_btn
99
104
  && self.col_type == rhs.col_type
100
105
  && self.metadata == rhs.metadata
@@ -162,11 +167,8 @@ impl Component for ActiveColumn {
162
167
  {
163
168
  let session = ctx.props().session.clone();
164
169
  let renderer = ctx.props().renderer.clone();
165
- if session.update_view_config(update).is_ok() {
166
- ApiFuture::spawn(async move {
167
- renderer.apply_pending_plugin()?;
168
- renderer.draw(session.validate().await?.create_view()).await
169
- });
170
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
171
+ spawn_owned("active-column", task);
170
172
  }
171
173
  }
172
174
 
@@ -190,11 +192,8 @@ impl Component for ActiveColumn {
190
192
  {
191
193
  let session = ctx.props().session.clone();
192
194
  let renderer = ctx.props().renderer.clone();
193
- if session.update_view_config(update).is_ok() {
194
- ApiFuture::spawn(async move {
195
- renderer.apply_pending_plugin()?;
196
- renderer.draw(session.validate().await?.create_view()).await
197
- });
195
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
196
+ spawn_owned("active-column", task);
198
197
  }
199
198
  }
200
199
 
@@ -210,10 +209,7 @@ impl Component for ActiveColumn {
210
209
  Named(String),
211
210
  }
212
211
 
213
- let mut classes = classes!["column-selector-draggable"];
214
- if ctx.props().is_aggregated {
215
- classes.push("show-aggregate");
216
- };
212
+ let mut classes = classes![];
217
213
 
218
214
  let mut outer_classes = classes!["column-selector-column"];
219
215
  if self.mouseover {
@@ -350,6 +346,7 @@ impl Component for ActiveColumn {
350
346
  .callback(|event: MouseEvent| MouseEnter(event.which() == 0));
351
347
 
352
348
  let is_expression = ctx.props().is_expression;
349
+ let is_window = ctx.props().is_window;
353
350
  let show_edit_btn = ctx.props().show_edit_btn;
354
351
  let mut class = ctx.props().renderer.metadata().select_mode.css();
355
352
  if is_required {
@@ -369,17 +366,14 @@ impl Component for ActiveColumn {
369
366
  ondragenter={ondragenter.clone()}
370
367
  >
371
368
  <span {class} onmousedown={remove_column} />
372
- <div
373
- class={classes}
374
- ref={&self.add_expression_ref}
375
- draggable="true"
376
- {ondragstart}
377
- {ondragend}
378
- >
379
- <div class="column-selector-column-border">
380
- <span class="drag-handle icon" />
381
- <TypeIcon ty={col_type} />
382
- if ctx.props().is_aggregated {
369
+ <ColumnSelectorColumnRow
370
+ name={name.clone()}
371
+ col_type={Some(col_type)}
372
+ wrapper_class={classes}
373
+ wrapper_ref={&self.add_expression_ref}
374
+ ondragstart={Some(ondragstart)}
375
+ ondragend={Some(ondragend.clone())}
376
+ aggregate={ctx.props().is_aggregated.then(|| html! {
383
377
  <AggregateSelector
384
378
  column={name.clone()}
385
379
  aggregate={ctx.props().get_aggregate(&name)}
@@ -388,24 +382,18 @@ impl Component for ActiveColumn {
388
382
  renderer={&ctx.props().renderer}
389
383
  session={&ctx.props().session}
390
384
  />
391
- }
392
- <span
393
- class="column_name"
394
- >
395
- { name.clone() }
396
- </span>
397
- if !ctx.props().is_aggregated {
398
- <span class="column-selector--spacer" />
399
- }
385
+ })}
386
+ trailing={html! {
400
387
  <ExprEditButton
388
+ {is_window}
401
389
  name={name.clone()}
402
390
  on_open_expr_panel={&ctx.props().on_open_expr_panel}
403
391
  {is_expression}
404
392
  is_disabled={!show_edit_btn}
405
393
  is_editing={ctx.props().is_editing}
406
394
  />
407
- </div>
408
- </div>
395
+ }}
396
+ />
409
397
  </div>
410
398
  }
411
399
  },
@@ -477,13 +465,8 @@ impl ActiveColumnProps {
477
465
  ..ViewConfigUpdate::default()
478
466
  };
479
467
 
480
- if self.session.update_view_config(config).is_ok() {
481
- let session = self.session.clone();
482
- let renderer = self.renderer.clone();
483
- ApiFuture::spawn(async move {
484
- renderer.apply_pending_plugin()?;
485
- renderer.draw(session.validate().await?.create_view()).await
486
- });
468
+ if let Ok(task) = apply_and_render(&self.session, &self.renderer, config) {
469
+ spawn_owned("active-column", task);
487
470
  }
488
471
  }
489
472
  }
@@ -14,13 +14,13 @@ use std::collections::HashSet;
14
14
  use std::rc::Rc;
15
15
 
16
16
  use perspective_client::config::*;
17
- use perspective_js::utils::ApiFuture;
18
17
  use yew::prelude::*;
19
18
 
20
19
  use crate::components::containers::select::*;
21
20
  use crate::renderer::*;
22
21
  use crate::session::*;
23
- use crate::utils::PtrEqRc;
22
+ use crate::tasks::apply_and_render;
23
+ use crate::utils::{PtrEqRc, spawn_owned};
24
24
 
25
25
  #[derive(Properties)]
26
26
  pub struct AggregateSelectorProps {
@@ -143,11 +143,8 @@ impl AggregateSelector {
143
143
 
144
144
  let session = ctx.props().session.clone();
145
145
  let renderer = ctx.props().renderer.clone();
146
- if session.update_view_config(config).is_ok() {
147
- ApiFuture::spawn(async move {
148
- renderer.apply_pending_plugin()?;
149
- renderer.draw(session.validate().await?.create_view()).await
150
- });
146
+ if let Ok(task) = apply_and_render(&session, &renderer, config) {
147
+ spawn_owned("aggregate-selector", task);
151
148
  }
152
149
  }
153
150
 
@@ -0,0 +1,73 @@
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
+ use web_sys::DragEvent;
15
+ use yew::prelude::*;
16
+
17
+ use crate::components::type_icon::TypeIcon;
18
+
19
+ /// The draggable row of a `column-selector-column`
20
+ #[derive(Clone, PartialEq, Properties)]
21
+ pub struct ColumnSelectorColumnRowProps {
22
+ pub name: String,
23
+
24
+ #[prop_or_default]
25
+ pub col_type: Option<ColumnType>,
26
+
27
+ #[prop_or_default]
28
+ pub aggregate: Option<Html>,
29
+
30
+ /// Trailing affordance (e.g. the edit button in the active list).
31
+ #[prop_or_default]
32
+ pub trailing: Html,
33
+
34
+ #[prop_or_default]
35
+ pub wrapper_class: Classes,
36
+
37
+ #[prop_or_default]
38
+ pub wrapper_ref: NodeRef,
39
+
40
+ #[prop_or_default]
41
+ pub ondragstart: Option<Callback<DragEvent>>,
42
+
43
+ #[prop_or_default]
44
+ pub ondragend: Option<Callback<DragEvent>>,
45
+ }
46
+
47
+ #[function_component]
48
+ pub fn ColumnSelectorColumnRow(p: &ColumnSelectorColumnRowProps) -> Html {
49
+ let mut classes = classes!["column-selector-draggable"];
50
+ if p.aggregate.is_some() {
51
+ classes.push("show-aggregate");
52
+ }
53
+
54
+ classes.extend(p.wrapper_class.clone());
55
+ html! {
56
+ <div
57
+ class={classes}
58
+ ref={&p.wrapper_ref}
59
+ draggable={p.ondragstart.is_some().then_some("true")}
60
+ ondragstart={p.ondragstart.clone()}
61
+ ondragend={p.ondragend.clone()}
62
+ >
63
+ <div class="column-selector-column-border">
64
+ <span class="drag-handle icon" />
65
+ <TypeIcon ty={p.col_type.unwrap_or(ColumnType::String)} />
66
+ if let Some(aggregate) = &p.aggregate { { aggregate.clone() } }
67
+ <span class="column_name">{ p.name.clone() }</span>
68
+ if p.aggregate.is_none() { <span class="column-selector--spacer" /> }
69
+ { p.trailing.clone() }
70
+ </div>
71
+ </div>
72
+ }
73
+ }
@@ -14,7 +14,6 @@ use std::collections::HashSet;
14
14
  use std::rc::Rc;
15
15
 
16
16
  use perspective_client::config::{ViewConfig, *};
17
- use perspective_js::utils::ApiFuture;
18
17
  use yew::prelude::*;
19
18
 
20
19
  use super::InPlaceColumn;
@@ -29,6 +28,7 @@ use crate::presentation::Presentation;
29
28
  use crate::renderer::*;
30
29
  use crate::session::drag_drop_update::*;
31
30
  use crate::session::*;
31
+ use crate::tasks::apply_and_render;
32
32
  use crate::utils::*;
33
33
 
34
34
  #[derive(Clone, Properties)]
@@ -132,11 +132,8 @@ impl Component for ConfigSelector {
132
132
  {
133
133
  let session = ctx.props().session.clone();
134
134
  let renderer = ctx.props().renderer.clone();
135
- if session.update_view_config(config).is_ok() {
136
- ApiFuture::spawn(async move {
137
- renderer.apply_pending_plugin()?;
138
- renderer.draw(session.validate().await?.create_view()).await
139
- });
135
+ if let Ok(task) = apply_and_render(&session, &renderer, config) {
136
+ spawn_owned("config-selector", task);
140
137
  }
141
138
  }
142
139
 
@@ -152,11 +149,8 @@ impl Component for ConfigSelector {
152
149
  {
153
150
  let session = ctx.props().session.clone();
154
151
  let renderer = ctx.props().renderer.clone();
155
- if session.update_view_config(config).is_ok() {
156
- ApiFuture::spawn(async move {
157
- renderer.apply_pending_plugin()?;
158
- renderer.draw(session.validate().await?.create_view()).await
159
- });
152
+ if let Ok(task) = apply_and_render(&session, &renderer, config) {
153
+ spawn_owned("config-selector", task);
160
154
  }
161
155
  }
162
156
 
@@ -191,11 +185,8 @@ impl Component for ConfigSelector {
191
185
  {
192
186
  let session = ctx.props().session.clone();
193
187
  let renderer = ctx.props().renderer.clone();
194
- if session.update_view_config(config).is_ok() {
195
- ApiFuture::spawn(async move {
196
- renderer.apply_pending_plugin()?;
197
- renderer.draw(session.validate().await?.create_view()).await
198
- });
188
+ if let Ok(task) = apply_and_render(&session, &renderer, config) {
189
+ spawn_owned("config-selector", task);
199
190
  }
200
191
  }
201
192
 
@@ -214,11 +205,8 @@ impl Component for ConfigSelector {
214
205
  {
215
206
  let session = ctx.props().session.clone();
216
207
  let renderer = ctx.props().renderer.clone();
217
- if session.update_view_config(config).is_ok() {
218
- ApiFuture::spawn(async move {
219
- renderer.apply_pending_plugin()?;
220
- renderer.draw(session.validate().await?.create_view()).await
221
- });
208
+ if let Ok(task) = apply_and_render(&session, &renderer, config) {
209
+ spawn_owned("config-selector", task);
222
210
  }
223
211
  }
224
212
 
@@ -237,11 +225,8 @@ impl Component for ConfigSelector {
237
225
  {
238
226
  let session = ctx.props().session.clone();
239
227
  let renderer = ctx.props().renderer.clone();
240
- if session.update_view_config(config).is_ok() {
241
- ApiFuture::spawn(async move {
242
- renderer.apply_pending_plugin()?;
243
- renderer.draw(session.validate().await?.create_view()).await
244
- });
228
+ if let Ok(task) = apply_and_render(&session, &renderer, config) {
229
+ spawn_owned("config-selector", task);
245
230
  }
246
231
  }
247
232
 
@@ -250,7 +235,7 @@ impl Component for ConfigSelector {
250
235
  },
251
236
  ConfigSelectorMsg::Close(..) => false,
252
237
  ConfigSelectorMsg::Drop(column, action, effect, index)
253
- if action != DragTarget::Active =>
238
+ if action != DragTarget::Active && !action.is_staged() =>
254
239
  {
255
240
  let col_type = ctx
256
241
  .props()
@@ -270,11 +255,8 @@ impl Component for ConfigSelector {
270
255
  {
271
256
  let session = ctx.props().session.clone();
272
257
  let renderer = ctx.props().renderer.clone();
273
- if session.update_view_config(update).is_ok() {
274
- ApiFuture::spawn(async move {
275
- renderer.apply_pending_plugin()?;
276
- renderer.draw(session.validate().await?.create_view()).await
277
- });
258
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
259
+ spawn_owned("config-selector", task);
278
260
  }
279
261
  }
280
262
 
@@ -300,11 +282,8 @@ impl Component for ConfigSelector {
300
282
  {
301
283
  let session = ctx.props().session.clone();
302
284
  let renderer = ctx.props().renderer.clone();
303
- if session.update_view_config(update).is_ok() {
304
- ApiFuture::spawn(async move {
305
- renderer.apply_pending_plugin()?;
306
- renderer.draw(session.validate().await?.create_view()).await
307
- });
285
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
286
+ spawn_owned("config-selector", task);
308
287
  }
309
288
  }
310
289
  ctx.props().onselect.emit(());
@@ -345,11 +324,8 @@ impl Component for ConfigSelector {
345
324
  {
346
325
  let session = ctx.props().session.clone();
347
326
  let renderer = ctx.props().renderer.clone();
348
- if session.update_view_config(update).is_ok() {
349
- ApiFuture::spawn(async move {
350
- renderer.apply_pending_plugin()?;
351
- renderer.draw(session.validate().await?.create_view()).await
352
- });
327
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
328
+ spawn_owned("config-selector", task);
353
329
  }
354
330
  }
355
331
 
@@ -366,11 +342,8 @@ impl Component for ConfigSelector {
366
342
  {
367
343
  let session = ctx.props().session.clone();
368
344
  let renderer = ctx.props().renderer.clone();
369
- if session.update_view_config(update).is_ok() {
370
- ApiFuture::spawn(async move {
371
- renderer.apply_pending_plugin()?;
372
- renderer.draw(session.validate().await?.create_view()).await
373
- });
345
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
346
+ spawn_owned("config-selector", task);
374
347
  }
375
348
  }
376
349
 
@@ -388,17 +361,20 @@ impl Component for ConfigSelector {
388
361
  {
389
362
  let session = ctx.props().session.clone();
390
363
  let renderer = ctx.props().renderer.clone();
391
- if session.update_view_config(update).is_ok() {
392
- ApiFuture::spawn(async move {
393
- renderer.apply_pending_plugin()?;
394
- renderer.draw(session.validate().await?.create_view()).await
395
- });
364
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
365
+ spawn_owned("config-selector", task);
396
366
  }
397
367
  }
398
368
 
399
369
  ctx.props().onselect.emit(());
400
370
  false
401
371
  },
372
+ ConfigSelectorMsg::New(
373
+ DragTarget::WindowSource
374
+ | DragTarget::WindowOrderBy
375
+ | DragTarget::WindowPartitionBy,
376
+ _,
377
+ ) => false,
402
378
  ConfigSelectorMsg::New(DragTarget::Filter, InPlaceColumn::Column(column)) => {
403
379
  let mut view_config = (*ctx.props().view_config).clone();
404
380
  let op = ctx.props().default_op(column.as_str()).unwrap_or_default();
@@ -416,11 +392,8 @@ impl Component for ConfigSelector {
416
392
  {
417
393
  let session = ctx.props().session.clone();
418
394
  let renderer = ctx.props().renderer.clone();
419
- if session.update_view_config(update).is_ok() {
420
- ApiFuture::spawn(async move {
421
- renderer.apply_pending_plugin()?;
422
- renderer.draw(session.validate().await?.create_view()).await
423
- });
395
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
396
+ spawn_owned("config-selector", task);
424
397
  }
425
398
  }
426
399
 
@@ -438,11 +411,8 @@ impl Component for ConfigSelector {
438
411
  {
439
412
  let session = ctx.props().session.clone();
440
413
  let renderer = ctx.props().renderer.clone();
441
- if session.update_view_config(update).is_ok() {
442
- ApiFuture::spawn(async move {
443
- renderer.apply_pending_plugin()?;
444
- renderer.draw(session.validate().await?.create_view()).await
445
- });
414
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
415
+ spawn_owned("config-selector", task);
446
416
  }
447
417
  }
448
418
 
@@ -462,11 +432,8 @@ impl Component for ConfigSelector {
462
432
  {
463
433
  let session = ctx.props().session.clone();
464
434
  let renderer = ctx.props().renderer.clone();
465
- if session.update_view_config(update).is_ok() {
466
- ApiFuture::spawn(async move {
467
- renderer.apply_pending_plugin()?;
468
- renderer.draw(session.validate().await?.create_view()).await
469
- });
435
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
436
+ spawn_owned("config-selector", task);
470
437
  }
471
438
  }
472
439
 
@@ -486,11 +453,8 @@ impl Component for ConfigSelector {
486
453
  {
487
454
  let session = ctx.props().session.clone();
488
455
  let renderer = ctx.props().renderer.clone();
489
- if session.update_view_config(update).is_ok() {
490
- ApiFuture::spawn(async move {
491
- renderer.apply_pending_plugin()?;
492
- renderer.draw(session.validate().await?.create_view()).await
493
- });
456
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
457
+ spawn_owned("config-selector", task);
494
458
  }
495
459
  }
496
460
 
@@ -518,11 +482,8 @@ impl Component for ConfigSelector {
518
482
  {
519
483
  let session = ctx.props().session.clone();
520
484
  let renderer = ctx.props().renderer.clone();
521
- if session.update_view_config(update).is_ok() {
522
- ApiFuture::spawn(async move {
523
- renderer.apply_pending_plugin()?;
524
- renderer.draw(session.validate().await?.create_view()).await
525
- });
485
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
486
+ spawn_owned("config-selector", task);
526
487
  }
527
488
  }
528
489
 
@@ -544,11 +505,8 @@ impl Component for ConfigSelector {
544
505
  {
545
506
  let session = ctx.props().session.clone();
546
507
  let renderer = ctx.props().renderer.clone();
547
- if session.update_view_config(update).is_ok() {
548
- ApiFuture::spawn(async move {
549
- renderer.apply_pending_plugin()?;
550
- renderer.draw(session.validate().await?.create_view()).await
551
- });
508
+ if let Ok(task) = apply_and_render(&session, &renderer, update) {
509
+ spawn_owned("config-selector", task);
552
510
  }
553
511
  }
554
512
 
@@ -22,6 +22,10 @@ pub struct ExprEditButtonProps {
22
22
  /// Is this an expression column?
23
23
  pub is_expression: bool,
24
24
 
25
+ /// Is this a window column?
26
+ #[prop_or_default]
27
+ pub is_window: bool,
28
+
25
29
  /// Fires when the config/expresison button is clicked.
26
30
  pub on_open_expr_panel: Callback<ColumnLocator>,
27
31
 
@@ -38,7 +42,9 @@ pub struct ExprEditButtonProps {
38
42
  #[function_component]
39
43
  pub fn ExprEditButton(p: &ExprEditButtonProps) -> Html {
40
44
  let onmousedown = yew::use_callback(p.clone(), |_, p| {
41
- let name = if p.is_expression {
45
+ let name = if p.is_window {
46
+ ColumnLocator::Window(p.name.clone())
47
+ } else if p.is_expression {
42
48
  ColumnLocator::Expression(p.name.clone())
43
49
  } else {
44
50
  ColumnLocator::Table(p.name.clone())
@@ -15,7 +15,6 @@ use std::rc::Rc;
15
15
 
16
16
  use chrono::{Datelike, NaiveDate, TimeZone, Utc};
17
17
  use perspective_client::config::*;
18
- use perspective_js::utils::ApiFuture;
19
18
  use wasm_bindgen::JsCast;
20
19
  use web_sys::*;
21
20
  use yew::prelude::*;
@@ -27,6 +26,7 @@ use crate::components::type_icon::TypeIcon;
27
26
  use crate::presentation::Presentation;
28
27
  use crate::renderer::*;
29
28
  use crate::session::*;
29
+ use crate::tasks::apply_and_render;
30
30
  use crate::utils::*;
31
31
 
32
32
  #[derive(Clone, Properties)]
@@ -448,13 +448,8 @@ impl FilterColumnProps {
448
448
  ..ViewConfigUpdate::default()
449
449
  };
450
450
 
451
- if self.session.update_view_config(update).is_ok() {
452
- let session = self.session.clone();
453
- let renderer = self.renderer.clone();
454
- ApiFuture::spawn(async move {
455
- renderer.apply_pending_plugin()?;
456
- renderer.draw(session.validate().await?.create_view()).await
457
- });
451
+ if let Ok(task) = apply_and_render(&self.session, &self.renderer, update) {
452
+ spawn_owned("filter-column", task);
458
453
  }
459
454
  }
460
455
 
@@ -526,13 +521,8 @@ impl FilterColumnProps {
526
521
  ..ViewConfigUpdate::default()
527
522
  };
528
523
 
529
- if self.session.update_view_config(update).is_ok() {
530
- let session = self.session.clone();
531
- let renderer = self.renderer.clone();
532
- ApiFuture::spawn(async move {
533
- renderer.apply_pending_plugin()?;
534
- renderer.draw(session.validate().await?.create_view()).await
535
- });
524
+ if let Ok(task) = apply_and_render(&self.session, &self.renderer, update) {
525
+ spawn_owned("filter-column", task);
536
526
  }
537
527
  }
538
528
  }
@@ -12,7 +12,6 @@
12
12
 
13
13
  use itertools::Itertools;
14
14
  use perspective_client::config::*;
15
- use perspective_js::utils::ApiFuture;
16
15
  use web_sys::*;
17
16
  use yew::prelude::*;
18
17
 
@@ -22,6 +21,7 @@ use crate::config::ColumnSelectMode;
22
21
  use crate::presentation::{ColumnLocator, Presentation};
23
22
  use crate::renderer::*;
24
23
  use crate::session::*;
24
+ use crate::tasks::apply_and_render;
25
25
  use crate::utils::*;
26
26
 
27
27
  #[derive(Clone, Properties)]
@@ -43,6 +43,10 @@ pub struct InactiveColumnProps {
43
43
  #[prop_or_default]
44
44
  pub is_expression: bool,
45
45
 
46
+ /// Whether this column is a window column.
47
+ #[prop_or_default]
48
+ pub is_window: bool,
49
+
46
50
  /// Session metadata snapshot — threaded from `SessionProps`.
47
51
  pub metadata: SessionMetadataRc,
48
52
 
@@ -72,6 +76,7 @@ impl PartialEq for InactiveColumnProps {
72
76
  && self.name == rhs.name
73
77
  && self.is_editing == rhs.is_editing
74
78
  && self.is_expression == rhs.is_expression
79
+ && self.is_window == rhs.is_window
75
80
  && self.metadata == rhs.metadata
76
81
  && self.view_config == rhs.view_config
77
82
  }
@@ -148,6 +153,7 @@ impl Component for InactiveColumn {
148
153
  .callback(|event: MouseEvent| MouseEnter(event.which() == 0));
149
154
 
150
155
  let is_expression = ctx.props().is_expression;
156
+ let is_window = ctx.props().is_window;
151
157
 
152
158
  let mut is_active_class = ctx.props().renderer.metadata().select_mode.css();
153
159
  is_active_class.push("shift-alt-icon");
@@ -179,7 +185,8 @@ impl Component for InactiveColumn {
179
185
  name={ctx.props().name.clone()}
180
186
  on_open_expr_panel={&ctx.props().on_open_expr_panel}
181
187
  {is_expression}
182
- is_disabled={!is_expression}
188
+ {is_window}
189
+ is_disabled={!(is_expression || is_window)}
183
190
  is_editing={ctx.props().is_editing}
184
191
  />
185
192
  </div>
@@ -231,13 +238,8 @@ impl InactiveColumnProps {
231
238
  ..ViewConfigUpdate::default()
232
239
  };
233
240
 
234
- if self.session.update_view_config(config).is_ok() {
235
- let session = self.session.clone();
236
- let renderer = self.renderer.clone();
237
- ApiFuture::spawn(async move {
238
- renderer.apply_pending_plugin()?;
239
- renderer.draw(session.validate().await?.create_view()).await
240
- });
241
+ if let Ok(task) = apply_and_render(&self.session, &self.renderer, config) {
242
+ spawn_owned("inactive-column", task);
241
243
  }
242
244
  }
243
245
  }