@perspective-dev/viewer 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -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/pro-dark.css +1 -1
  17. package/dist/css/pro.css +1 -1
  18. package/dist/css/solarized-dark.css +1 -1
  19. package/dist/css/solarized.css +1 -1
  20. package/dist/css/themes.css +1 -1
  21. package/dist/css/vaporwave.css +1 -1
  22. package/dist/esm/perspective-viewer.inline.js +2 -2
  23. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  24. package/dist/esm/perspective-viewer.js +2 -2
  25. package/dist/esm/perspective-viewer.js.map +4 -4
  26. package/dist/wasm/perspective-viewer.d.ts +57 -53
  27. package/dist/wasm/perspective-viewer.js +190 -165
  28. package/dist/wasm/perspective-viewer.wasm +0 -0
  29. package/dist/wasm/perspective-viewer.wasm.d.ts +17 -18
  30. package/package.json +7 -5
  31. package/src/{less/aggregate-selector.less → css/aggregate-selector.css} +23 -20
  32. package/src/css/column-dropdown.css +109 -0
  33. package/src/{less/column-selector.less → css/column-selector.css} +160 -158
  34. package/src/{less/column-settings-panel.less → css/column-settings-panel.css} +69 -59
  35. package/src/{less/column-style.less → css/column-style.css} +52 -66
  36. package/src/{less/column-symbol-attributes.less → css/column-symbol-attributes.css} +15 -14
  37. package/src/{less/config-selector.less → css/config-selector.css} +151 -135
  38. package/src/{less/containers/dropdown-menu.less → css/containers/dropdown-menu.css} +20 -19
  39. package/src/{less/containers/pairs-list.less → css/containers/pairs-list.css} +13 -12
  40. package/src/{themes/variables.less → css/containers/scroll-panel.css} +25 -22
  41. package/src/{less/containers/split-panel.less → css/containers/split-panel.css} +15 -14
  42. package/src/{less/containers/tabs.less → css/containers/tabs.css} +17 -19
  43. package/src/css/dom/checkbox.css +102 -0
  44. package/src/css/dom/scrollbar.css +35 -0
  45. package/src/{less/dom/select.less → css/dom/select.css} +17 -18
  46. package/src/{less/empty-column.less → css/empty-column.css} +19 -18
  47. package/src/{less/expression-editor.less → css/expression-editor.css} +19 -18
  48. package/src/{less/filter-dropdown.less → css/filter-dropdown.css} +12 -11
  49. package/src/{less/filter-item.less → css/filter-item.css} +16 -15
  50. package/src/{less/form/code-editor.less → css/form/code-editor.css} +26 -30
  51. package/src/{less/form/debug.less → css/form/debug.css} +19 -18
  52. package/src/{less/function-dropdown.less → css/function-dropdown.css} +12 -11
  53. package/src/css/plugin-selector.css +261 -0
  54. package/src/{less/render-warning.less → css/render-warning.css} +18 -17
  55. package/src/{less/status-bar.less → css/status-bar.css} +156 -144
  56. package/src/css/type-icon.css +116 -0
  57. package/src/{less/viewer.less → css/viewer.css} +112 -146
  58. package/src/rust/components/column_dropdown.rs +229 -119
  59. package/src/rust/components/column_selector/active_column.rs +81 -62
  60. package/src/rust/components/column_selector/add_expression_button.rs +1 -0
  61. package/src/rust/components/column_selector/aggregate_selector.rs +25 -15
  62. package/src/rust/components/column_selector/config_selector.rs +315 -199
  63. package/src/rust/components/column_selector/empty_column.rs +2 -2
  64. package/src/rust/components/column_selector/expr_edit_button.rs +8 -2
  65. package/src/rust/components/column_selector/filter_column.rs +37 -26
  66. package/src/rust/components/column_selector/inactive_column.rs +41 -29
  67. package/src/rust/components/column_selector/invalid_column.rs +7 -18
  68. package/src/rust/components/column_selector/pivot_column.rs +11 -5
  69. package/src/rust/components/column_selector/sort_column.rs +23 -13
  70. package/src/rust/components/column_selector.rs +163 -84
  71. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +1 -1
  72. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +3 -2
  73. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs_item.rs +3 -2
  74. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +2 -3
  75. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +7 -1
  76. package/src/rust/components/column_settings_sidebar/style_tab.rs +153 -112
  77. package/src/rust/components/column_settings_sidebar.rs +91 -53
  78. package/src/rust/components/containers/dragdrop_list.rs +2 -1
  79. package/src/rust/components/containers/sidebar_close_button.rs +1 -1
  80. package/src/rust/components/containers/split_panel.rs +1 -0
  81. package/src/rust/components/containers/tab_list.rs +1 -1
  82. package/src/rust/components/copy_dropdown.rs +7 -28
  83. package/src/rust/components/datetime_column_style/custom.rs +2 -2
  84. package/src/rust/components/datetime_column_style/simple.rs +2 -2
  85. package/src/rust/components/datetime_column_style.rs +4 -2
  86. package/src/rust/components/editable_header.rs +7 -4
  87. package/src/rust/components/empty_row.rs +1 -1
  88. package/src/rust/components/export_dropdown.rs +4 -30
  89. package/src/rust/components/expression_editor.rs +19 -10
  90. package/src/rust/components/filter_dropdown.rs +246 -102
  91. package/src/rust/components/font_loader.rs +11 -28
  92. package/src/rust/components/form/code_editor.rs +17 -2
  93. package/src/rust/components/form/color_range_selector.rs +19 -6
  94. package/src/rust/components/form/debug.rs +30 -13
  95. package/src/rust/components/function_dropdown.rs +186 -113
  96. package/src/rust/components/main_panel.rs +71 -89
  97. package/src/rust/components/mod.rs +1 -1
  98. package/src/rust/components/modal.rs +7 -1
  99. package/src/rust/components/number_column_style.rs +22 -7
  100. package/src/rust/components/plugin_selector.rs +34 -102
  101. package/src/rust/components/portal.rs +274 -0
  102. package/src/rust/components/render_warning.rs +72 -123
  103. package/src/rust/components/settings_panel.rs +115 -11
  104. package/src/rust/components/status_bar.rs +222 -98
  105. package/src/rust/components/status_bar_counter.rs +8 -20
  106. package/src/rust/components/status_indicator.rs +64 -114
  107. package/src/rust/components/string_column_style.rs +2 -2
  108. package/src/rust/components/style/style_cache.rs +5 -1
  109. package/src/rust/components/viewer.rs +391 -39
  110. package/src/rust/custom_elements/copy_dropdown.rs +102 -21
  111. package/src/rust/custom_elements/export_dropdown.rs +102 -20
  112. package/src/rust/custom_elements/mod.rs +0 -7
  113. package/src/rust/custom_elements/modal.rs +7 -103
  114. package/src/rust/custom_elements/viewer.rs +99 -35
  115. package/src/rust/custom_events.rs +23 -2
  116. package/src/rust/dragdrop.rs +149 -10
  117. package/src/{less/containers/scroll-panel.less → rust/engines.rs} +15 -13
  118. package/src/rust/js/plugin.rs +1 -1
  119. package/src/rust/lib.rs +5 -4
  120. package/src/rust/presentation/props.rs +39 -0
  121. package/src/rust/presentation/sheets.rs +3 -3
  122. package/src/rust/presentation.rs +44 -8
  123. package/src/rust/renderer/limits.rs +32 -3
  124. package/src/{less/dom/scrollbar.less → rust/renderer/props.rs} +18 -19
  125. package/src/rust/renderer.rs +83 -9
  126. package/src/rust/session/column_defaults_update.rs +1 -1
  127. package/src/rust/session/metadata.rs +23 -2
  128. package/src/rust/session/props.rs +178 -0
  129. package/src/rust/session.rs +124 -117
  130. package/src/rust/tasks/column_locator.rs +133 -0
  131. package/src/rust/{model → tasks}/columns_iter_set.rs +14 -23
  132. package/src/rust/{model → tasks}/edit_expression.rs +34 -10
  133. package/src/rust/{model → tasks}/eject.rs +2 -2
  134. package/src/rust/{model → tasks}/get_viewer_config.rs +0 -11
  135. package/src/rust/{model → tasks}/intersection_observer.rs +19 -3
  136. package/src/{less/containers/radio-list.less → rust/tasks/is_invalid_drop.rs} +21 -14
  137. package/src/rust/tasks/mod.rs +52 -0
  138. package/src/rust/{model → tasks}/plugin_column_styles.rs +69 -46
  139. package/src/rust/{model → tasks}/resize_observer.rs +39 -6
  140. package/src/rust/{model → tasks}/send_plugin_config.rs +1 -1
  141. package/src/rust/tasks/structural.rs +53 -0
  142. package/src/rust/utils/mod.rs +4 -0
  143. package/src/rust/utils/modal_position.rs +110 -0
  144. package/src/rust/utils/ptr_eq_rc.rs +74 -0
  145. package/src/rust/utils/pubsub.rs +11 -1
  146. package/src/svg/bg-pattern.png +0 -0
  147. package/src/svg/close-icon.svg +1 -1
  148. package/src/svg/expression.svg +1 -1
  149. package/src/svg/mega-menu-icons-candlestick.svg +1 -1
  150. package/src/svg/mega-menu-icons-datagrid.svg +1 -2
  151. package/src/svg/mega-menu-icons-heatmap.svg +1 -1
  152. package/src/svg/mega-menu-icons-map-scatter.svg +1 -1
  153. package/src/svg/mega-menu-icons-ohlc.svg +1 -1
  154. package/src/svg/mega-menu-icons-sunburst.svg +1 -1
  155. package/src/svg/mega-menu-icons-treemap.svg +1 -1
  156. package/src/svg/mega-menu-icons-x-bar.svg +1 -1
  157. package/src/svg/mega-menu-icons-x-y-line.svg +1 -1
  158. package/src/svg/mega-menu-icons-x-y-scatter.svg +1 -1
  159. package/src/svg/mega-menu-icons-y-area.svg +1 -1
  160. package/src/svg/mega-menu-icons-y-bar.svg +1 -1
  161. package/src/svg/mega-menu-icons-y-line.svg +1 -1
  162. package/src/svg/mega-menu-icons-y-scatter.svg +1 -1
  163. package/src/svg/radio-hover.svg +1 -1
  164. package/src/svg/radio-off.svg +1 -1
  165. package/src/svg/radio-on.svg +1 -1
  166. package/src/themes/botanical.css +157 -0
  167. package/src/themes/defaults.css +139 -0
  168. package/src/themes/dracula.css +233 -0
  169. package/src/themes/gruvbox-dark.css +255 -0
  170. package/src/themes/gruvbox.css +134 -0
  171. package/src/themes/icons.css +124 -0
  172. package/src/themes/intl/de.css +102 -0
  173. package/src/themes/intl/es.css +102 -0
  174. package/src/themes/intl/fr.css +102 -0
  175. package/src/themes/intl/ja.css +102 -0
  176. package/src/themes/intl/pt.css +102 -0
  177. package/src/themes/intl/zh.css +102 -0
  178. package/src/themes/intl.css +102 -0
  179. package/src/themes/monokai.css +233 -0
  180. package/src/themes/pro-dark.css +158 -0
  181. package/src/themes/{themes.less → pro.css} +17 -21
  182. package/src/themes/solarized-dark.css +135 -0
  183. package/src/themes/solarized.css +95 -0
  184. package/src/themes/themes.css +22 -0
  185. package/src/themes/vaporwave.css +256 -0
  186. package/dist/css/variables.css +0 -0
  187. package/src/less/column-dropdown.less +0 -95
  188. package/src/less/dom/checkbox.less +0 -100
  189. package/src/less/plugin-selector.less +0 -183
  190. package/src/less/type-icon.less +0 -68
  191. package/src/rust/components/error_message.rs +0 -56
  192. package/src/rust/custom_elements/column_dropdown.rs +0 -123
  193. package/src/rust/custom_elements/filter_dropdown.rs +0 -179
  194. package/src/rust/custom_elements/function_dropdown.rs +0 -115
  195. package/src/rust/model/column_locator.rs +0 -82
  196. package/src/rust/model/is_invalid_drop.rs +0 -36
  197. package/src/rust/model/mod.rs +0 -100
  198. package/src/rust/model/reset_all.rs +0 -38
  199. package/src/rust/model/structural.rs +0 -244
  200. package/src/themes/botanical.less +0 -142
  201. package/src/themes/dracula.less +0 -101
  202. package/src/themes/gruvbox-dark.less +0 -116
  203. package/src/themes/gruvbox.less +0 -152
  204. package/src/themes/icons.less +0 -130
  205. package/src/themes/intl/de.less +0 -102
  206. package/src/themes/intl/es.less +0 -102
  207. package/src/themes/intl/fr.less +0 -102
  208. package/src/themes/intl/ja.less +0 -102
  209. package/src/themes/intl/pt.less +0 -102
  210. package/src/themes/intl/zh.less +0 -102
  211. package/src/themes/intl.less +0 -102
  212. package/src/themes/monokai.less +0 -107
  213. package/src/themes/pro-dark.less +0 -147
  214. package/src/themes/pro.less +0 -186
  215. package/src/themes/solarized-dark.less +0 -78
  216. package/src/themes/solarized.less +0 -102
  217. package/src/themes/vaporwave.less +0 -145
  218. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline0.js +0 -0
  219. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline1.js +0 -0
  220. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline2.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline3.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d729f682ba5c19df → perspective-viewer-68fef752754ffbc6}/inline4.js +0 -0
  223. /package/src/rust/{model → tasks}/copy_export.rs +0 -0
  224. /package/src/rust/{model → tasks}/export_app.rs +0 -0
  225. /package/src/rust/{model → tasks}/export_method.rs +0 -0
  226. /package/src/rust/{model → tasks}/restore_and_render.rs +0 -0
  227. /package/src/rust/{model → tasks}/update_and_render.rs +0 -0
@@ -0,0 +1,133 @@
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, ViewConfig};
14
+
15
+ use super::HasSession;
16
+ use super::plugin_column_styles::can_render_column_styles;
17
+ use crate::presentation::{ColumnLocator, OpenColumnSettings};
18
+ use crate::renderer::Renderer;
19
+ use crate::session::SessionMetadata;
20
+ use crate::tasks::{HasPresentation, HasRenderer, PluginColumnStyles};
21
+
22
+ /// Returns the column name for a locator, generating a default for new
23
+ /// expressions.
24
+ pub fn locator_name_or_default(metadata: &SessionMetadata, locator: &ColumnLocator) -> String {
25
+ match locator {
26
+ ColumnLocator::Table(s) | ColumnLocator::Expression(s) => s.clone(),
27
+ ColumnLocator::NewExpression => metadata.make_new_column_name(None),
28
+ }
29
+ }
30
+
31
+ /// Returns the view type for a locator's column, if available.
32
+ pub fn locator_view_type(
33
+ metadata: &SessionMetadata,
34
+ locator: &ColumnLocator,
35
+ ) -> Option<ColumnType> {
36
+ let name = locator.name().cloned().unwrap_or_default();
37
+ metadata.get_column_view_type(name.as_str())
38
+ }
39
+
40
+ /// Returns a [`ColumnLocator`] for a given column name, or [`None`] if no
41
+ /// such column exists.
42
+ pub fn get_column_locator(
43
+ metadata: &SessionMetadata,
44
+ name: Option<String>,
45
+ ) -> Option<ColumnLocator> {
46
+ name.and_then(|name| {
47
+ if metadata.is_column_expression(&name) {
48
+ Some(ColumnLocator::Expression(name))
49
+ } else {
50
+ metadata.get_table_columns().and_then(|x| {
51
+ x.iter()
52
+ .find_map(|n| (n == &name).then_some(ColumnLocator::Table(name.clone())))
53
+ })
54
+ }
55
+ })
56
+ }
57
+
58
+ /// Gets a [`ColumnLocator`] for the current UI's column settings state,
59
+ /// or [`None`] if it is not currently active.
60
+ pub fn get_current_column_locator(
61
+ open_column_settings: &OpenColumnSettings,
62
+ renderer: &Renderer,
63
+ view_config: &ViewConfig,
64
+ metadata: &SessionMetadata,
65
+ ) -> Option<ColumnLocator> {
66
+ open_column_settings
67
+ .locator
68
+ .clone()
69
+ .filter(|locator| match locator {
70
+ ColumnLocator::Table(name) => {
71
+ locator
72
+ .name()
73
+ .map(|name| {
74
+ view_config.columns.iter().any(|maybe_col| {
75
+ maybe_col
76
+ .as_ref()
77
+ .map(|col| col == name)
78
+ .unwrap_or_default()
79
+ }) || view_config.group_by.iter().any(|col| col == name)
80
+ || view_config.split_by.iter().any(|col| col == name)
81
+ || view_config.filter.iter().any(|col| col.column() == name)
82
+ || view_config.sort.iter().any(|col| &col.0 == name)
83
+ })
84
+ .unwrap_or_default()
85
+ && can_render_column_styles(renderer, view_config, metadata, name)
86
+ .unwrap_or_default()
87
+ },
88
+ _ => true,
89
+ })
90
+ }
91
+
92
+ /// Trait facade — delegates to standalone functions above.
93
+ pub trait ColumnLocatorExt: HasSession {
94
+ fn locator_name_or_default(&self, locator: &ColumnLocator) -> String {
95
+ locator_name_or_default(&self.session().metadata(), locator)
96
+ }
97
+
98
+ fn is_locator_active(&self, locator: &ColumnLocator) -> bool {
99
+ locator
100
+ .name()
101
+ .map(|name| self.session().to_props().is_column_active(name))
102
+ .unwrap_or_default()
103
+ }
104
+
105
+ fn locator_view_type(&self, locator: &ColumnLocator) -> Option<ColumnType> {
106
+ locator_view_type(&self.session().metadata(), locator)
107
+ }
108
+
109
+ fn get_column_locator(&self, name: Option<String>) -> Option<ColumnLocator> {
110
+ get_column_locator(&self.session().metadata(), name)
111
+ }
112
+ }
113
+
114
+ impl<T: HasSession> ColumnLocatorExt for T {}
115
+
116
+ /// Trait facade for `get_current_column_locator`.
117
+ pub trait ColumnLocatorCurrentExt:
118
+ HasPresentation + HasRenderer + HasSession + PluginColumnStyles
119
+ {
120
+ fn get_current_column_locator(&self) -> Option<ColumnLocator> {
121
+ get_current_column_locator(
122
+ &self.presentation().get_open_column_settings(),
123
+ self.renderer(),
124
+ &self.session().get_view_config(),
125
+ &self.session().metadata(),
126
+ )
127
+ }
128
+ }
129
+
130
+ impl<T: HasPresentation + HasRenderer + HasSession + PluginColumnStyles> ColumnLocatorCurrentExt
131
+ for T
132
+ {
133
+ }
@@ -15,10 +15,10 @@ use std::collections::HashSet;
15
15
  use itertools::Itertools;
16
16
  use perspective_client::config::*;
17
17
 
18
- use super::{HasRenderer, HasSession, IsInvalidDrop};
18
+ use super::is_invalid_columns_column;
19
19
  use crate::dragdrop::*;
20
20
  use crate::renderer::*;
21
- use crate::session::*;
21
+ use crate::session::SessionMetadata;
22
22
  use crate::utils::DragTarget;
23
23
  use crate::*;
24
24
 
@@ -65,28 +65,16 @@ type Label = Option<String>;
65
65
  /// iterator returning functions `active()`, `inactive()` and `expression()`.
66
66
  pub struct ColumnsIteratorSet<'a> {
67
67
  config: &'a ViewConfig,
68
- session: &'a Session,
69
68
  renderer: &'a Renderer,
70
- metadata: MetadataRef<'a>,
69
+ metadata: &'a SessionMetadata,
71
70
  is_dragover_column: Option<(usize, String)>,
72
71
  named_columns: Vec<String>,
73
72
  }
74
73
 
75
- impl HasRenderer for ColumnsIteratorSet<'_> {
76
- fn renderer(&self) -> &'_ Renderer {
77
- self.renderer
78
- }
79
- }
80
- impl HasSession for ColumnsIteratorSet<'_> {
81
- fn session(&self) -> &'_ Session {
82
- self.session
83
- }
84
- }
85
-
86
74
  impl<'a> ColumnsIteratorSet<'a> {
87
75
  pub fn new(
88
76
  config: &'a ViewConfig,
89
- session: &'a Session,
77
+ metadata: &'a SessionMetadata,
90
78
  renderer: &'a Renderer,
91
79
  dragdrop: &DragDrop,
92
80
  ) -> ColumnsIteratorSet<'a> {
@@ -94,9 +82,8 @@ impl<'a> ColumnsIteratorSet<'a> {
94
82
  let named_columns = renderer.metadata().names.clone().unwrap_or_default();
95
83
  ColumnsIteratorSet {
96
84
  config,
97
- session,
98
85
  renderer,
99
- metadata: session.metadata(),
86
+ metadata,
100
87
  is_dragover_column,
101
88
  named_columns,
102
89
  }
@@ -117,10 +104,9 @@ impl<'a> ColumnsIteratorSet<'a> {
117
104
  || self.config.columns.len() < self.named_columns.len()
118
105
  || self.config.columns.iter().filter(|x| x.is_some()).count()
119
106
  == self
120
- .session
121
- .metadata()
107
+ .metadata
122
108
  .get_table_columns()
123
- .map(|x| x.len())
109
+ .map(|x: &Vec<String>| x.len())
124
110
  .unwrap_or_default()
125
111
  + self.config.expressions.len();
126
112
 
@@ -143,7 +129,12 @@ impl<'a> ColumnsIteratorSet<'a> {
143
129
  .map(|x| self.renderer.metadata().is_swap(x))
144
130
  .unwrap_or_default();
145
131
 
146
- let is_swap_invalid = self.is_invalid_columns_column(from_column, *to_index);
132
+ let is_swap_invalid = is_invalid_columns_column(
133
+ self.config,
134
+ self.renderer.metadata().min,
135
+ from_column,
136
+ *to_index,
137
+ );
147
138
  let is_dragover_after_last = *to_index == self.config.columns.len();
148
139
  if is_swap_invalid {
149
140
  let all_columns = self.config.columns.iter().map(|x| x.into());
@@ -299,7 +290,7 @@ impl<'a> ColumnsIteratorSet<'a> {
299
290
  };
300
291
 
301
292
  if !is_active || is_swap_over.unwrap_or_default() {
302
- let col_type = self.session.metadata().get_column_table_type(name)?;
293
+ let col_type = self.metadata.get_column_table_type(name)?;
303
294
  let is_visible = dragover_col.is_none_or(|(_, x)| x != name);
304
295
  Some(OrderedColumn {
305
296
  is_visible,
@@ -19,13 +19,31 @@ use crate::renderer::Renderer;
19
19
  use crate::session::Session;
20
20
  use crate::*;
21
21
 
22
- #[derive(PartialEq, PerspectiveProperties!)]
22
+ #[derive(Clone, PartialEq)]
23
23
  pub struct ExpressionUpdater {
24
24
  presentation: Presentation,
25
25
  renderer: Renderer,
26
26
  session: Session,
27
27
  }
28
28
 
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
+ }
45
+ }
46
+
29
47
  pub trait EditExpression: HasPresentation + HasRenderer + HasSession + UpdateAndRender {
30
48
  fn get_expression_updater(&self) -> ExpressionUpdater {
31
49
  ExpressionUpdater {
@@ -41,37 +59,43 @@ pub trait EditExpression: HasPresentation + HasRenderer + HasSession + UpdateAnd
41
59
  ApiFuture::spawn(async move {
42
60
  let update = this
43
61
  .session
44
- .create_replace_expression_update(&old_name, &new_expr)
45
- .await;
62
+ .to_props()
63
+ .create_replace_expression_update(&old_name, &new_expr);
46
64
 
65
+ this.update_and_render(update)?.await?;
47
66
  this.presentation
48
67
  .set_open_column_settings(Some(OpenColumnSettings {
49
68
  locator: Some(ColumnLocator::Expression(new_expr.name.to_string())),
50
69
  tab: Some(ColumnSettingsTab::Attributes),
51
70
  }));
52
71
 
53
- this.update_and_render(update)?.await?;
54
72
  Ok(())
55
73
  });
56
74
  }
57
75
 
58
76
  /// Saves a new expression. Spawns a future.
59
77
  fn save_expr(&self, expr: Expression) -> ApiResult<()> {
78
+ let presentation = self.presentation().clone();
79
+ let expr_name: String = expr.name.clone().into();
60
80
  let task = {
61
81
  let mut serde_exprs = self.session().get_view_config().expressions.clone();
62
82
  serde_exprs.insert(&expr);
63
- self.presentation()
64
- .set_open_column_settings(Some(OpenColumnSettings {
65
- locator: Some(ColumnLocator::Expression(expr.name.clone().into())),
66
- tab: Some(ColumnSettingsTab::Attributes),
67
- }));
68
83
  self.update_and_render(ViewConfigUpdate {
69
84
  expressions: Some(serde_exprs),
70
85
  ..Default::default()
71
86
  })
72
87
  }?;
73
88
 
74
- ApiFuture::spawn(task);
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
+ }));
95
+
96
+ Ok(())
97
+ });
98
+
75
99
  Ok(())
76
100
  }
77
101
 
@@ -16,7 +16,7 @@ use yew::Component;
16
16
  use super::{HasRenderer, HasSession};
17
17
  use crate::ApiFuture;
18
18
  use crate::root::Root;
19
- use crate::session::ResetOptions;
19
+ use crate::session::{ResetOptions, TableIntermediateState};
20
20
 
21
21
  pub trait DeleteAll: HasSession + HasRenderer {
22
22
  fn delete_all<T: Component>(&self, root: &Root<T>) -> ApiFuture<()> {
@@ -29,7 +29,7 @@ pub trait DeleteAll: HasSession + HasRenderer {
29
29
  .reset(ResetOptions {
30
30
  config: true,
31
31
  expressions: true,
32
- table: true,
32
+ table: Some(TableIntermediateState::Ejected),
33
33
  ..ResetOptions::default()
34
34
  })
35
35
  .await?;
@@ -10,9 +10,6 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use perspective_client::config::ViewConfig;
14
-
15
- use super::columns_iter_set::*;
16
13
  use super::structural::*;
17
14
  use crate::config::*;
18
15
  use crate::*;
@@ -51,11 +48,3 @@ pub trait GetViewerConfigModel: HasSession + HasRenderer + HasPresentation {
51
48
  }
52
49
 
53
50
  impl<T: HasRenderer + HasSession + HasPresentation> GetViewerConfigModel for T {}
54
-
55
- pub trait ColumnIteratorModel: HasSession + HasRenderer + HasDragDrop {
56
- fn column_selector_iter_set<'a>(&'a self, config: &'a ViewConfig) -> ColumnsIteratorSet<'a> {
57
- ColumnsIteratorSet::new(config, self.session(), self.renderer(), self.dragdrop())
58
- }
59
- }
60
-
61
- impl<T: HasSession + HasRenderer + HasDragDrop> ColumnIteratorModel for T {}
@@ -16,10 +16,10 @@ use web_sys::*;
16
16
 
17
17
  use crate::config::ViewerConfigUpdate;
18
18
  use crate::js::*;
19
- use crate::model::*;
20
19
  use crate::presentation::Presentation;
21
20
  use crate::renderer::*;
22
21
  use crate::session::Session;
22
+ use crate::tasks::*;
23
23
  use crate::utils::*;
24
24
  use crate::*;
25
25
 
@@ -71,18 +71,34 @@ impl Drop for IntersectionObserverHandle {
71
71
  }
72
72
  }
73
73
 
74
- #[derive(PerspectiveProperties!)]
75
74
  struct IntersectionObserverState {
76
75
  session: Session,
77
76
  renderer: Renderer,
78
77
  presentation: Presentation,
79
78
  }
80
79
 
80
+ impl HasPresentation for IntersectionObserverState {
81
+ fn presentation(&self) -> &Presentation {
82
+ &self.presentation
83
+ }
84
+ }
85
+
86
+ impl HasRenderer for IntersectionObserverState {
87
+ fn renderer(&self) -> &Renderer {
88
+ &self.renderer
89
+ }
90
+ }
91
+
92
+ impl HasSession for IntersectionObserverState {
93
+ fn session(&self) -> &Session {
94
+ &self.session
95
+ }
96
+ }
97
+
81
98
  impl IntersectionObserverState {
82
99
  async fn set_pause(self, intersect: bool) -> ApiResult<()> {
83
100
  if intersect {
84
101
  if self.session.set_pause(false) {
85
- self.presentation.visibility_changed.emit(intersect);
86
102
  self.restore_and_render(ViewerConfigUpdate::default(), async move { Ok(()) })
87
103
  .await?;
88
104
  }
@@ -10,20 +10,27 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- :host {
14
- .radio-list-item {
15
- display: flex;
16
- flex-wrap: wrap;
17
- align-items: center;
13
+ use perspective_client::config::ViewConfig;
18
14
 
19
- // Fix radio buttons
20
- input[type="radio"] {
21
- width: 16px;
22
- margin: 0 4px 0 0;
23
- }
15
+ /// Checks whether dropping `from_column` at `to_index` in the active columns
16
+ /// list would be invalid (i.e. removing a required column to an empty slot).
17
+ pub fn is_invalid_columns_column(
18
+ config: &ViewConfig,
19
+ min: Option<usize>,
20
+ from_column: &String,
21
+ to_index: usize,
22
+ ) -> bool {
23
+ let from_index = config
24
+ .columns
25
+ .iter()
26
+ .position(|x| x.as_ref() == Some(from_column));
24
27
 
25
- input[type="radio"][disabled] ~ * {
26
- opacity: 0.5;
27
- }
28
- }
28
+ let is_to_empty = !config
29
+ .columns
30
+ .get(to_index)
31
+ .map(|x| x.is_some())
32
+ .unwrap_or_default();
33
+ min.and_then(|x| from_index.map(|from_index| from_index < x))
34
+ .unwrap_or_default()
35
+ && is_to_empty
29
36
  }
@@ -0,0 +1,52 @@
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
+ //! Async tasks and model traits for coordinating state between `Session`,
14
+ //! `Renderer`, `Presentation`, and `DragDrop` singletons.
15
+ //!
16
+ //! Complex operations that span more than one state object are expressed as
17
+ //! traits whose bounds name exactly the state objects they require (e.g.
18
+ //! `UpdateAndRender: HasSession + HasRenderer`). Blanket impls apply the
19
+ //! trait to any struct that holds those fields and provides `Has*` accessors.
20
+
21
+ mod column_locator;
22
+ mod columns_iter_set;
23
+ mod copy_export;
24
+ mod edit_expression;
25
+ mod eject;
26
+ mod export_app;
27
+ mod export_method;
28
+ mod get_viewer_config;
29
+ mod intersection_observer;
30
+ mod is_invalid_drop;
31
+ mod plugin_column_styles;
32
+ mod resize_observer;
33
+ mod restore_and_render;
34
+ mod send_plugin_config;
35
+ mod structural;
36
+ mod update_and_render;
37
+
38
+ pub use self::column_locator::*;
39
+ pub use self::columns_iter_set::*;
40
+ pub use self::copy_export::*;
41
+ pub use self::edit_expression::*;
42
+ pub use self::eject::*;
43
+ pub use self::export_method::*;
44
+ pub use self::get_viewer_config::*;
45
+ pub use self::intersection_observer::*;
46
+ pub use self::is_invalid_drop::is_invalid_columns_column;
47
+ pub use self::plugin_column_styles::*;
48
+ pub use self::resize_observer::*;
49
+ pub use self::restore_and_render::*;
50
+ pub use self::send_plugin_config::*;
51
+ pub use self::structural::*;
52
+ pub use self::update_and_render::*;
@@ -11,64 +11,87 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  use itertools::Itertools;
14
+ use perspective_client::config::ViewConfig;
14
15
  use perspective_js::utils::ApiResult;
15
16
 
16
17
  use super::{HasRenderer, HasSession};
17
18
  use crate::config::ColumnStyleOpts;
19
+ use crate::renderer::Renderer;
20
+ use crate::session::SessionMetadata;
18
21
 
19
- pub trait PluginColumnStyles: HasSession + HasRenderer {
20
- /// This function will query the plugin to see if a given column can render
21
- /// column styles.
22
- fn can_render_column_styles(&self, column_name: &str) -> ApiResult<bool> {
23
- let plugin = self.renderer().get_active_plugin()?;
24
- let names: Vec<String> = plugin
25
- .config_column_names()
26
- .and_then(|jsarr| serde_wasm_bindgen::from_value(jsarr.into()).ok())
27
- .unwrap_or_default();
22
+ /// Queries the plugin to see if a given column can render column styles.
23
+ pub fn can_render_column_styles(
24
+ renderer: &Renderer,
25
+ view_config: &ViewConfig,
26
+ metadata: &SessionMetadata,
27
+ column_name: &str,
28
+ ) -> ApiResult<bool> {
29
+ let plugin = renderer.get_active_plugin()?;
30
+ let names: Vec<String> = plugin
31
+ .config_column_names()
32
+ .and_then(|jsarr| serde_wasm_bindgen::from_value(jsarr.into()).ok())
33
+ .unwrap_or_default();
28
34
 
29
- let group = self
30
- .session()
31
- .get_view_config()
32
- .columns
33
- .iter()
34
- .find_position(|maybe_s| maybe_s.as_deref() == Some(column_name))
35
- .and_then(|(idx, _)| names.get(idx))
36
- .map(|s| s.as_str());
35
+ let group = view_config
36
+ .columns
37
+ .iter()
38
+ .find_position(|maybe_s| maybe_s.as_deref() == Some(column_name))
39
+ .and_then(|(idx, _)| names.get(idx))
40
+ .map(|s| s.as_str());
37
41
 
38
- let view_type = self
39
- .session()
40
- .metadata()
41
- .get_column_view_type(column_name)
42
- .ok_or("Invalid column")?;
42
+ let view_type = metadata
43
+ .get_column_view_type(column_name)
44
+ .ok_or("Invalid column")?;
43
45
 
44
- plugin.can_render_column_styles(&view_type.to_string(), group)
45
- }
46
+ plugin.can_render_column_styles(&view_type.to_string(), group)
47
+ }
46
48
 
47
- /// Queries the plugins for the available plugin components.
48
- fn get_column_style_control_options(&self, column_name: &str) -> ApiResult<ColumnStyleOpts> {
49
- let plugin = self.renderer().get_active_plugin()?;
50
- let names: Vec<String> = plugin
51
- .config_column_names()
52
- .and_then(|jsarr| serde_wasm_bindgen::from_value(jsarr.into()).ok())
53
- .unwrap_or_default();
49
+ /// Queries the plugin for the available style control options for a column.
50
+ pub fn get_column_style_control_options(
51
+ renderer: &Renderer,
52
+ view_config: &ViewConfig,
53
+ metadata: &SessionMetadata,
54
+ column_name: &str,
55
+ ) -> ApiResult<ColumnStyleOpts> {
56
+ let plugin = renderer.get_active_plugin()?;
57
+ let names: Vec<String> = plugin
58
+ .config_column_names()
59
+ .and_then(|jsarr| serde_wasm_bindgen::from_value(jsarr.into()).ok())
60
+ .unwrap_or_default();
61
+
62
+ let group = view_config
63
+ .columns
64
+ .iter()
65
+ .find_position(|maybe_s| maybe_s.as_deref() == Some(column_name))
66
+ .and_then(|(idx, _)| names.get(idx))
67
+ .map(|s| s.as_str());
54
68
 
55
- let group = self
56
- .session()
57
- .get_view_config()
58
- .columns
59
- .iter()
60
- .find_position(|maybe_s| maybe_s.as_deref() == Some(column_name))
61
- .and_then(|(idx, _)| names.get(idx))
62
- .map(|s| s.as_str());
69
+ let view_type = metadata
70
+ .get_column_view_type(column_name)
71
+ .ok_or("Invalid column")?;
63
72
 
64
- let view_type = self
65
- .session()
66
- .metadata()
67
- .get_column_view_type(column_name)
68
- .ok_or("Invalid column")?;
73
+ let controls = plugin.column_style_controls(&view_type.to_string(), group)?;
74
+ serde_wasm_bindgen::from_value(controls).map_err(|e| e.into())
75
+ }
76
+
77
+ /// Trait facade — delegates to the standalone functions above.
78
+ pub trait PluginColumnStyles: HasSession + HasRenderer {
79
+ fn can_render_column_styles(&self, column_name: &str) -> ApiResult<bool> {
80
+ can_render_column_styles(
81
+ self.renderer(),
82
+ &self.session().get_view_config(),
83
+ &self.session().metadata(),
84
+ column_name,
85
+ )
86
+ }
69
87
 
70
- let controls = plugin.column_style_controls(&view_type.to_string(), group)?;
71
- serde_wasm_bindgen::from_value(controls).map_err(|e| e.into())
88
+ fn get_column_style_control_options(&self, column_name: &str) -> ApiResult<ColumnStyleOpts> {
89
+ get_column_style_control_options(
90
+ self.renderer(),
91
+ &self.session().get_view_config(),
92
+ &self.session().metadata(),
93
+ column_name,
94
+ )
72
95
  }
73
96
  }
74
97