@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
@@ -10,24 +10,48 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use futures::channel::oneshot::*;
14
13
  use perspective_js::utils::*;
15
14
  use wasm_bindgen::prelude::*;
16
15
  use yew::prelude::*;
17
16
 
18
17
  use super::render_warning::RenderWarning;
19
18
  use super::status_bar::StatusBar;
20
- use crate::PerspectiveProperties;
21
19
  use crate::custom_events::CustomEvents;
22
20
  use crate::presentation::Presentation;
21
+ use crate::renderer::limits::RenderLimits;
23
22
  use crate::renderer::*;
24
- use crate::session::*;
23
+ use crate::session::{Session, TableErrorState, TableLoadState, ViewStats};
25
24
  use crate::utils::*;
26
25
 
27
- #[derive(Clone, Properties, PerspectiveProperties!)]
26
+ #[derive(Clone, Properties)]
28
27
  pub struct MainPanelProps {
29
28
  pub on_settings: Callback<()>,
30
29
 
30
+ /// Reset callback forwarded from the root component. Fired when the user
31
+ /// clicks the reset button; `bool` is `true` for a full reset (expressions
32
+ /// + column configs), `false` for config-only.
33
+ pub on_reset: Callback<bool>,
34
+
35
+ /// Render-limit dimensions forwarded from the root's `RendererProps`.
36
+ /// `Some` when the active plugin is capping the rendered row/column count;
37
+ /// `None` when no limits are active (e.g. after a plugin change).
38
+ pub render_limits: Option<RenderLimits>,
39
+
40
+ /// Value props from root's `SessionProps`, threaded to `StatusBar` /
41
+ /// `StatusIndicator`.
42
+ pub has_table: Option<TableLoadState>,
43
+ pub is_errored: bool,
44
+ pub stats: Option<ViewStats>,
45
+ pub update_count: u32,
46
+ pub error: Option<TableErrorState>,
47
+ pub title: Option<String>,
48
+
49
+ /// Value props from root's `PresentationProps`, threaded to `StatusBar`.
50
+ pub is_settings_open: bool,
51
+ pub selected_theme: Option<String>,
52
+ pub available_themes: PtrEqRc<Vec<String>>,
53
+ pub is_workspace: bool,
54
+
31
55
  /// State
32
56
  pub custom_events: CustomEvents,
33
57
  pub session: Session,
@@ -36,28 +60,33 @@ pub struct MainPanelProps {
36
60
  }
37
61
 
38
62
  impl PartialEq for MainPanelProps {
39
- fn eq(&self, _rhs: &Self) -> bool {
40
- false
63
+ fn eq(&self, rhs: &Self) -> bool {
64
+ self.has_table == rhs.has_table
65
+ && self.is_errored == rhs.is_errored
66
+ && self.stats == rhs.stats
67
+ && self.update_count == rhs.update_count
68
+ && self.error == rhs.error
69
+ && self.title == rhs.title
70
+ && self.is_settings_open == rhs.is_settings_open
71
+ && self.selected_theme == rhs.selected_theme
72
+ && self.available_themes == rhs.available_themes
73
+ && self.is_workspace == rhs.is_workspace
74
+ && self.render_limits == rhs.render_limits
41
75
  }
42
76
  }
43
77
 
44
78
  impl MainPanelProps {
45
79
  fn is_title(&self) -> bool {
46
- self.session.get_title().is_some()
80
+ self.title.is_some()
47
81
  }
48
82
  }
49
83
 
50
84
  #[derive(Debug)]
51
85
  pub enum MainPanelMsg {
52
- Reset(bool, Option<Sender<()>>),
53
- RenderLimits(Option<(usize, usize, Option<usize>, Option<usize>)>),
54
86
  PointerEvent(web_sys::PointerEvent),
55
- Error,
56
87
  }
57
88
 
58
89
  pub struct MainPanel {
59
- _subscriptions: [Subscription; 2],
60
- dimensions: Option<(usize, usize, Option<usize>, Option<usize>)>,
61
90
  main_panel_ref: NodeRef,
62
91
  }
63
92
 
@@ -65,85 +94,14 @@ impl Component for MainPanel {
65
94
  type Message = MainPanelMsg;
66
95
  type Properties = MainPanelProps;
67
96
 
68
- fn create(ctx: &Context<Self>) -> Self {
69
- let session_sub = {
70
- let callback = ctx.link().callback(move |(_, render_limits)| {
71
- MainPanelMsg::RenderLimits(Some(render_limits))
72
- });
73
-
74
- ctx.props()
75
- .renderer
76
- .render_limits_changed
77
- .add_listener(callback)
78
- };
79
-
80
- let error_sub = ctx
81
- .props()
82
- .session
83
- .table_errored
84
- .add_listener(ctx.link().callback(|_| MainPanelMsg::Error));
85
-
97
+ fn create(_ctx: &Context<Self>) -> Self {
86
98
  Self {
87
- _subscriptions: [session_sub, error_sub],
88
- dimensions: None,
89
99
  main_panel_ref: NodeRef::default(),
90
100
  }
91
101
  }
92
102
 
93
103
  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
94
104
  match msg {
95
- MainPanelMsg::Error => true,
96
- MainPanelMsg::Reset(all, sender) => {
97
- ctx.props().presentation.set_open_column_settings(None);
98
-
99
- clone!(
100
- ctx.props().renderer,
101
- ctx.props().session,
102
- ctx.props().presentation
103
- );
104
-
105
- ApiFuture::spawn(async move {
106
- session
107
- .reset(ResetOptions {
108
- config: true,
109
- expressions: all,
110
- ..ResetOptions::default()
111
- })
112
- .await?;
113
- let columns_config = if all {
114
- presentation.reset_columns_configs();
115
- None
116
- } else {
117
- Some(presentation.all_columns_configs())
118
- };
119
-
120
- renderer.reset(columns_config.as_ref()).await?;
121
- presentation.reset_available_themes(None).await;
122
- if all {
123
- presentation.reset_theme().await?;
124
- }
125
-
126
- let result = renderer.draw(session.validate().await?.create_view()).await;
127
- if let Some(sender) = sender {
128
- sender.send(()).unwrap();
129
- }
130
-
131
- renderer.reset_changed.emit(());
132
- result
133
- });
134
-
135
- false
136
- },
137
-
138
- MainPanelMsg::RenderLimits(dimensions) => {
139
- if self.dimensions != dimensions {
140
- self.dimensions = dimensions;
141
- true
142
- } else {
143
- false
144
- }
145
- },
146
-
147
105
  MainPanelMsg::PointerEvent(event) => {
148
106
  if event.target().map(JsValue::from)
149
107
  == self
@@ -175,8 +133,8 @@ impl Component for MainPanel {
175
133
  ..
176
134
  } = ctx.props();
177
135
 
178
- let is_settings_open =
179
- ctx.props().presentation.is_settings_open() && ctx.props().session.has_table();
136
+ let is_settings_open = ctx.props().is_settings_open
137
+ && matches!(ctx.props().has_table, Some(TableLoadState::Loaded));
180
138
 
181
139
  let on_settings = (!is_settings_open).then(|| ctx.props().on_settings.clone());
182
140
 
@@ -189,14 +147,35 @@ impl Component for MainPanel {
189
147
  class.push("titled");
190
148
  }
191
149
 
192
- let on_reset = ctx.link().callback(|all| MainPanelMsg::Reset(all, None));
193
150
  let pointerdown = ctx.link().callback(MainPanelMsg::PointerEvent);
151
+ let on_dismiss_warning = {
152
+ clone!(renderer, session);
153
+ Callback::from(move |_: ()| {
154
+ clone!(renderer, session);
155
+ ApiFuture::spawn(async move {
156
+ renderer.disable_active_plugin_render_warning();
157
+ let view_task = session.get_view();
158
+ renderer.update(view_task).await
159
+ });
160
+ })
161
+ };
162
+
194
163
  html! {
195
164
  <div id="main_column">
196
165
  <StatusBar
197
166
  id="status_bar"
198
167
  {on_settings}
199
- on_reset={on_reset.clone()}
168
+ on_reset={ctx.props().on_reset.clone()}
169
+ has_table={ctx.props().has_table.clone()}
170
+ is_errored={ctx.props().is_errored}
171
+ stats={ctx.props().stats.clone()}
172
+ update_count={ctx.props().update_count}
173
+ error={ctx.props().error.clone()}
174
+ title={ctx.props().title.clone()}
175
+ is_settings_open={ctx.props().is_settings_open}
176
+ selected_theme={ctx.props().selected_theme.clone()}
177
+ available_themes={ctx.props().available_themes.clone()}
178
+ is_workspace={ctx.props().is_workspace}
200
179
  {custom_events}
201
180
  {presentation}
202
181
  {renderer}
@@ -208,7 +187,10 @@ impl Component for MainPanel {
208
187
  {class}
209
188
  onpointerdown={pointerdown}
210
189
  >
211
- <RenderWarning {renderer} {session} dimensions={self.dimensions} />
190
+ <RenderWarning
191
+ on_dismiss={on_dismiss_warning}
192
+ dimensions={ctx.props().render_limits}
193
+ />
212
194
  <slot />
213
195
  </div>
214
196
  </div>
@@ -22,7 +22,6 @@ pub mod copy_dropdown;
22
22
  pub mod datetime_column_style;
23
23
  pub mod editable_header;
24
24
  pub mod empty_row;
25
- pub mod error_message;
26
25
  pub mod export_dropdown;
27
26
  pub mod expression_editor;
28
27
  pub mod filter_dropdown;
@@ -33,6 +32,7 @@ pub mod main_panel;
33
32
  pub mod modal;
34
33
  pub mod number_column_style;
35
34
  pub mod plugin_selector;
35
+ pub mod portal;
36
36
  pub mod render_warning;
37
37
  pub mod settings_panel;
38
38
  pub mod status_bar;
@@ -78,7 +78,7 @@ where
78
78
  } => {
79
79
  let opacity = if visible { "" } else { ";opacity:0" };
80
80
  self.css = format!(":host{{top:{top}px;left:{left}px{opacity}}}");
81
- self.rev_vert.0.set(rev_vert);
81
+ self.rev_vert.set(rev_vert);
82
82
  true
83
83
  },
84
84
  ModalMsg::SubMsg(msg) => {
@@ -117,6 +117,12 @@ pub struct ModalOrientation(Rc<Cell<bool>>);
117
117
 
118
118
  impl ImplicitClone for ModalOrientation {}
119
119
 
120
+ impl ModalOrientation {
121
+ pub fn set(&self, value: bool) {
122
+ self.0.set(value);
123
+ }
124
+ }
125
+
120
126
  impl From<ModalOrientation> for bool {
121
127
  fn from(x: ModalOrientation) -> Self {
122
128
  x.0.get()
@@ -10,6 +10,7 @@
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::Scalar;
13
14
  use yew::prelude::*;
14
15
  use yew::*;
15
16
 
@@ -65,8 +66,25 @@ impl ModalLink<NumberColumnStyle> for NumberColumnStyleProps {
65
66
  }
66
67
 
67
68
  impl PartialEq for NumberColumnStyleProps {
68
- fn eq(&self, _other: &Self) -> bool {
69
- false
69
+ fn eq(&self, other: &Self) -> bool {
70
+ self.config == other.config
71
+ && self.default_config == other.default_config
72
+ && self.column_name == other.column_name
73
+ }
74
+ }
75
+
76
+ fn scalar_to_f64(scalar: &Scalar) -> f64 {
77
+ match scalar {
78
+ Scalar::Float(x) => *x,
79
+ Scalar::String(x) => x.parse::<f64>().unwrap_or_default(),
80
+ Scalar::Bool(x) => {
81
+ if *x {
82
+ 1.0
83
+ } else {
84
+ 0.0
85
+ }
86
+ },
87
+ Scalar::Null => 0.0,
70
88
  }
71
89
  }
72
90
 
@@ -76,12 +94,9 @@ fn set_default_gradient(session: &Session, ctx: &Context<NumberColumnStyle>) {
76
94
  ctx.link().send_future(async move {
77
95
  let view = session.get_view().unwrap();
78
96
  let min_max = view.get_min_max(column_name).await.unwrap();
79
- let abs_max = min_max
80
- .0
81
- .parse::<f64>()
82
- .unwrap_or_default()
97
+ let abs_max = scalar_to_f64(&min_max.0)
83
98
  .abs()
84
- .max(min_max.1.parse::<f64>().unwrap_or_default().abs());
99
+ .max(scalar_to_f64(&min_max.1).abs());
85
100
 
86
101
  let gradient = (abs_max * 100.).round() / 100.;
87
102
  NumberColumnStyleMsg::DefaultGradientChanged(gradient)
@@ -10,106 +10,52 @@
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::ViewConfigUpdate;
14
- use perspective_js::utils::ApiFuture;
15
13
  use yew::prelude::*;
16
14
 
17
- use super::containers::select::*;
18
15
  use super::style::LocalStyle;
19
- use crate::config::*;
20
- use crate::js::*;
21
- use crate::model::*;
22
- use crate::presentation::Presentation;
23
- use crate::renderer::*;
24
- use crate::session::*;
25
- use crate::utils::*;
26
- use crate::{css, *};
27
-
28
- #[derive(Properties, PartialEq, PerspectiveProperties!)]
16
+ use crate::css;
17
+ use crate::utils::PtrEqRc;
18
+
19
+ /// Pure value props — no engine handles, no PubSub subscriptions.
20
+ /// The parent passes updated values whenever the renderer state changes.
21
+ #[derive(Properties, PartialEq)]
29
22
  pub struct PluginSelectorProps {
30
- pub presentation: Presentation,
31
- pub renderer: Renderer,
32
- pub session: Session,
23
+ /// Name of the currently active plugin.
24
+ pub plugin_name: Option<String>,
25
+
26
+ /// Flat list of all registered plugin names (all categories merged).
27
+ pub available_plugins: PtrEqRc<Vec<String>>,
28
+
29
+ /// Called when the user selects a different plugin.
30
+ pub on_select_plugin: Callback<String>,
33
31
  }
34
32
 
35
33
  #[derive(Debug)]
36
34
  pub enum PluginSelectorMsg {
37
35
  ComponentSelectPlugin(String),
38
- RendererSelectPlugin(String),
39
36
  OpenMenu,
40
37
  }
41
38
 
42
39
  use PluginSelectorMsg::*;
43
40
 
44
41
  pub struct PluginSelector {
45
- options: Vec<SelectItem<String>>,
46
42
  is_open: bool,
47
- _plugin_sub: Subscription,
48
43
  }
49
44
 
50
45
  impl Component for PluginSelector {
51
46
  type Message = PluginSelectorMsg;
52
47
  type Properties = PluginSelectorProps;
53
48
 
54
- fn create(ctx: &Context<Self>) -> Self {
55
- let PluginSelectorProps { renderer, .. } = ctx.props();
56
- let options = generate_plugin_optgroups(renderer);
57
- let _plugin_sub = renderer.plugin_changed.add_listener({
58
- let link = ctx.link().clone();
59
- move |plugin: JsPerspectiveViewerPlugin| {
60
- let name = plugin.name();
61
- link.send_message(PluginSelectorMsg::RendererSelectPlugin(name))
62
- }
63
- });
64
-
65
- Self {
66
- options,
67
- is_open: false,
68
- _plugin_sub,
69
- }
49
+ fn create(_ctx: &Context<Self>) -> Self {
50
+ Self { is_open: false }
70
51
  }
71
52
 
72
53
  fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
73
- let PluginSelectorProps {
74
- presentation,
75
- renderer,
76
- session,
77
- ..
78
- } = ctx.props();
79
54
  match msg {
80
- RendererSelectPlugin(_plugin_name) => true,
81
55
  ComponentSelectPlugin(plugin_name) => {
82
- if !session.is_errored() {
83
- let metadata =
84
- renderer.get_next_plugin_metadata(&PluginUpdate::Update(plugin_name));
85
-
86
- let prev_metadata = renderer.metadata();
87
- let requirements = metadata.as_ref().unwrap_or(&*prev_metadata);
88
- let rollup_features = session
89
- .metadata()
90
- .get_features()
91
- .map(|x| x.get_group_rollup_modes())
92
- .unwrap();
93
-
94
- let group_rollups = requirements.get_group_rollups(&rollup_features);
95
- let mut update = ViewConfigUpdate {
96
- group_rollup_mode: group_rollups.first().cloned(),
97
- ..ViewConfigUpdate::default()
98
- };
99
-
100
- session.set_update_column_defaults(&mut update, requirements);
101
-
102
- if let Ok(task) = ctx.props().update_and_render(update) {
103
- ApiFuture::spawn(task);
104
- }
105
-
106
- presentation.set_open_column_settings(None);
107
- self.is_open = false;
108
- false
109
- } else {
110
- self.is_open = false;
111
- true
112
- }
56
+ ctx.props().on_select_plugin.emit(plugin_name);
57
+ self.is_open = false;
58
+ false
113
59
  },
114
60
  OpenMenu => {
115
61
  self.is_open = !self.is_open;
@@ -124,22 +70,18 @@ impl Component for PluginSelector {
124
70
 
125
71
  fn view(&self, ctx: &Context<Self>) -> Html {
126
72
  let callback = ctx.link().callback(|_| OpenMenu);
127
- let plugin_name = ctx.props().renderer.get_active_plugin().unwrap().name();
73
+ let plugin_name = ctx.props().plugin_name.clone().unwrap_or_default();
128
74
  let plugin_name2 = plugin_name.clone();
129
75
  let class = if self.is_open { "open" } else { "" };
130
- let items = self.options.iter().map(|item| match item {
131
- SelectItem::OptGroup(_cat, items) => html! {
132
- items.iter().filter(|x| *x != &plugin_name2).map(|x| {
133
- let callback = ctx.link().callback(ComponentSelectPlugin);
134
- html! {
135
- <PluginSelect
136
- name={ x.to_owned() }
137
- on_click={ callback } />
138
- }
139
- }).collect::<Html>()
140
- },
141
- SelectItem::Option(_item) => html! {},
142
- });
76
+ let items = ctx
77
+ .props()
78
+ .available_plugins
79
+ .iter()
80
+ .filter(|x| x.as_str() != plugin_name2.as_str())
81
+ .map(|x| {
82
+ let callback = ctx.link().callback(ComponentSelectPlugin);
83
+ html! { <PluginSelect name={x.to_owned()} on_click={callback} /> }
84
+ });
143
85
 
144
86
  html! {
145
87
  <>
@@ -148,7 +90,9 @@ impl Component for PluginSelector {
148
90
  <PluginSelect name={plugin_name} on_click={callback} />
149
91
  <div id="plugin_selector_border" />
150
92
  if self.is_open {
151
- <div class="plugin-selector-options">{ items.collect::<Html>() }</div>
93
+ <div class="plugin-selector-options scrollable">
94
+ { items.collect::<Html>() }
95
+ </div>
152
96
  }
153
97
  </div>
154
98
  </>
@@ -156,19 +100,6 @@ impl Component for PluginSelector {
156
100
  }
157
101
  }
158
102
 
159
- /// Generate the opt groups for the plugin selector by collecting by category
160
- /// then sorting.
161
- fn generate_plugin_optgroups(renderer: &Renderer) -> Vec<SelectItem<String>> {
162
- let mut options = renderer
163
- .get_all_plugin_categories()
164
- .into_iter()
165
- .map(|(category, value)| SelectItem::OptGroup(category.into(), value))
166
- .collect::<Vec<_>>();
167
-
168
- options.sort_by_key(|x| x.name());
169
- options
170
- }
171
-
172
103
  #[derive(Properties, PartialEq)]
173
104
  struct PluginSelectProps {
174
105
  name: String,
@@ -194,9 +125,10 @@ fn PluginSelect(props: &PluginSelectProps) -> Html {
194
125
  <div
195
126
  class="plugin-select-item"
196
127
  data-plugin={name.clone()}
197
- style={format!("--default-column-title:var(--plugin-name-{}--content, \"{}\")", path, props.name)}
128
+ style={format!("--default-column-title:var(--psp-plugin-name--{}--content, \"{}\")", path, props.name)}
198
129
  onclick={props.on_click.reform(move |_| name.clone())}
199
130
  >
131
+ <span class="icon" />
200
132
  <span class="plugin-select-item-name" />
201
133
  </div>
202
134
  }