@perspective-dev/viewer 4.4.1 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/cdn/perspective-viewer.js +1 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/esm/bootstrap.d.ts +2 -1
  24. package/dist/esm/column-format.d.ts +51 -0
  25. package/dist/esm/extensions.d.ts +2 -0
  26. package/dist/esm/perspective-viewer.d.ts +3 -1
  27. package/dist/esm/perspective-viewer.inline.js +1 -2
  28. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  29. package/dist/esm/perspective-viewer.js +1 -2
  30. package/dist/esm/perspective-viewer.js.map +4 -4
  31. package/dist/esm/perspective-viewer.worker.d.ts +2 -0
  32. package/dist/esm/plugin.d.ts +16 -72
  33. package/dist/esm/ts-rs/ColumnSelectMode.d.ts +1 -0
  34. package/dist/esm/ts-rs/PluginStaticConfig.d.ts +77 -0
  35. package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -3
  36. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -4
  37. package/dist/wasm/perspective-viewer.d.ts +77 -18
  38. package/dist/wasm/perspective-viewer.js +302 -148
  39. package/dist/wasm/perspective-viewer.wasm +0 -0
  40. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -15
  41. package/package.json +24 -2
  42. package/src/css/column-selector.css +3 -2
  43. package/src/css/column-settings-panel.css +44 -9
  44. package/src/css/column-style.css +35 -2
  45. package/src/css/containers/scroll-panel.css +2 -1
  46. package/src/css/containers/tabs.css +8 -52
  47. package/src/css/dom/checkbox.css +2 -6
  48. package/src/css/form/code-editor.css +1 -0
  49. package/src/css/form/debug.css +3 -10
  50. package/src/css/plugin-selector.css +33 -0
  51. package/src/css/plugin-settings-panel.css +99 -0
  52. package/src/css/viewer.css +143 -3
  53. package/src/rust/components/column_dropdown.rs +3 -1
  54. package/src/rust/components/column_selector/active_column.rs +16 -19
  55. package/src/rust/components/column_selector/config_selector.rs +20 -20
  56. package/src/rust/components/column_selector/filter_column.rs +14 -14
  57. package/src/rust/components/column_selector/inactive_column.rs +10 -15
  58. package/src/rust/components/column_selector/pivot_column.rs +7 -7
  59. package/src/rust/components/column_selector/sort_column.rs +7 -7
  60. package/src/rust/components/column_selector.rs +55 -37
  61. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +15 -7
  62. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +395 -0
  63. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +15 -6
  64. package/src/rust/components/column_settings_sidebar/style_tab.rs +267 -136
  65. package/src/rust/components/column_settings_sidebar.rs +44 -49
  66. package/src/rust/components/containers/dragdrop_list.rs +32 -5
  67. package/src/rust/components/containers/mod.rs +0 -1
  68. package/src/rust/components/containers/scroll_panel.rs +21 -7
  69. package/src/rust/components/containers/sidebar.rs +8 -6
  70. package/src/rust/components/containers/split_panel.rs +3 -3
  71. package/src/rust/components/containers/tab_list.rs +3 -9
  72. package/src/rust/components/copy_dropdown.rs +2 -3
  73. package/src/rust/components/datetime_column_style.rs +19 -81
  74. package/src/rust/components/editable_header.rs +17 -3
  75. package/src/rust/components/export_dropdown.rs +2 -3
  76. package/src/rust/components/expression_editor.rs +29 -17
  77. package/src/rust/components/filter_dropdown.rs +2 -1
  78. package/src/rust/components/form/color_range_selector.rs +14 -7
  79. package/src/rust/components/form/debug.rs +47 -37
  80. package/src/rust/components/main_panel.rs +24 -65
  81. package/src/rust/components/mod.rs +2 -1
  82. package/src/rust/components/number_series_style.rs +161 -0
  83. package/src/rust/components/plugin_tab.rs +221 -0
  84. package/src/rust/components/settings_panel.rs +181 -59
  85. package/src/rust/components/status_bar.rs +141 -174
  86. package/src/rust/components/status_indicator.rs +15 -22
  87. package/src/rust/components/string_column_style.rs +20 -82
  88. package/src/rust/components/style_controls/number_string_format.rs +14 -30
  89. package/src/rust/components/viewer.rs +169 -132
  90. package/src/rust/config/column_config_schema.rs +195 -0
  91. package/src/rust/config/columns_config.rs +4 -97
  92. package/src/rust/config/datetime_column_style.rs +0 -5
  93. package/src/rust/config/mod.rs +8 -2
  94. package/src/rust/config/number_series_style.rs +79 -0
  95. package/src/rust/config/plugin_static_config.rs +144 -0
  96. package/src/rust/config/string_column_style.rs +0 -5
  97. package/src/rust/config/viewer_config.rs +5 -6
  98. package/src/rust/custom_elements/copy_dropdown.rs +30 -18
  99. package/src/rust/custom_elements/debug_plugin.rs +1 -3
  100. package/src/rust/custom_elements/export_dropdown.rs +26 -18
  101. package/src/rust/custom_elements/viewer.rs +62 -73
  102. package/src/rust/custom_events.rs +181 -224
  103. package/src/rust/js/plugin.rs +45 -117
  104. package/src/rust/lib.rs +34 -5
  105. package/src/rust/presentation/drag_helpers.rs +206 -0
  106. package/src/rust/presentation/props.rs +8 -0
  107. package/src/rust/presentation.rs +256 -41
  108. package/src/rust/{tasks → queries}/column_locator.rs +17 -73
  109. package/src/rust/queries/column_values.rs +59 -0
  110. package/src/rust/{tasks → queries}/columns_iter_set.rs +11 -18
  111. package/src/rust/queries/exports.rs +96 -0
  112. package/src/rust/queries/fetch_column_stats.rs +94 -0
  113. package/src/rust/queries/get_viewer_config.rs +54 -0
  114. package/src/rust/queries/mod.rs +44 -0
  115. package/src/rust/queries/plugin_column_styles.rs +101 -0
  116. package/src/rust/{engines.rs → queries/validate_expression.rs} +26 -15
  117. package/src/rust/renderer/activate.rs +1 -0
  118. package/src/rust/renderer/limits.rs +9 -4
  119. package/src/rust/renderer/plugin_store.rs +12 -0
  120. package/src/rust/renderer/props.rs +28 -3
  121. package/src/rust/renderer/registry.rs +40 -15
  122. package/src/rust/renderer.rs +703 -60
  123. package/src/rust/session/column_defaults_update.rs +20 -28
  124. package/src/rust/session/drag_drop_update.rs +10 -10
  125. package/src/rust/session/metadata.rs +31 -16
  126. package/src/rust/session/props.rs +15 -6
  127. package/src/rust/session/view_subscription.rs +10 -0
  128. package/src/rust/session.rs +109 -147
  129. package/src/rust/tasks/copy_export.rs +178 -158
  130. package/src/rust/tasks/{structural.rs → dismiss_render_warning.rs} +20 -40
  131. package/src/rust/tasks/edit_expression.rs +68 -88
  132. package/src/rust/tasks/eject.rs +25 -22
  133. package/src/rust/tasks/intersection_observer.rs +8 -21
  134. package/src/rust/tasks/mod.rs +19 -21
  135. package/src/rust/tasks/reset_all.rs +98 -0
  136. package/src/rust/tasks/resize_observer.rs +11 -33
  137. package/src/rust/tasks/restore_and_render.rs +128 -90
  138. package/src/rust/tasks/{get_viewer_config.rs → send_column_config.rs} +39 -35
  139. package/src/rust/tasks/send_plugin_config.rs +33 -33
  140. package/src/rust/tasks/update_and_render.rs +75 -49
  141. package/src/rust/{components/containers/trap_door_panel.rs → tasks/update_theme.rs} +34 -33
  142. package/src/rust/tasks/validate_expression.rs +61 -0
  143. package/src/rust/utils/browser/selection.rs +4 -4
  144. package/src/rust/utils/mod.rs +0 -63
  145. package/src/svg/checkbox-checked-icon.svg +1 -1
  146. package/src/svg/checkbox-unchecked-icon.svg +1 -1
  147. package/src/svg/mega-menu-icons-density.svg +23 -0
  148. package/src/svg/mega-menu-icons-map-density.svg +24 -0
  149. package/src/svg/mega-menu-icons-map-line.svg +19 -0
  150. package/src/themes/botanical.css +27 -53
  151. package/src/themes/defaults.css +24 -36
  152. package/src/themes/dracula.css +36 -54
  153. package/src/themes/gruvbox-dark.css +39 -59
  154. package/src/themes/gruvbox.css +16 -28
  155. package/src/themes/icons.css +5 -0
  156. package/src/themes/intl/de.css +43 -6
  157. package/src/themes/intl/es.css +43 -6
  158. package/src/themes/intl/fr.css +43 -6
  159. package/src/themes/intl/ja.css +43 -6
  160. package/src/themes/intl/pt.css +43 -6
  161. package/src/themes/intl/zh.css +43 -6
  162. package/src/themes/intl.css +38 -4
  163. package/src/themes/monokai.css +45 -61
  164. package/src/themes/phosphor.css +20 -29
  165. package/src/themes/pro-dark.css +25 -34
  166. package/src/themes/solarized-dark.css +21 -36
  167. package/src/themes/solarized.css +13 -23
  168. package/src/themes/vaporwave.css +40 -74
  169. package/src/ts/bootstrap.ts +14 -3
  170. package/src/ts/column-format.ts +162 -0
  171. package/src/ts/extensions.ts +4 -0
  172. package/src/ts/perspective-viewer.ts +9 -1
  173. package/src/{rust/components/column_settings_sidebar/style_tab/stub.rs → ts/perspective-viewer.worker.ts} +2 -22
  174. package/src/ts/plugin.ts +25 -101
  175. package/src/ts/ts-rs/{FormatUnit.ts → ColumnSelectMode.ts} +1 -1
  176. package/src/ts/ts-rs/PluginStaticConfig.ts +78 -0
  177. package/src/ts/ts-rs/ViewerConfig.ts +1 -2
  178. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -3
  179. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +0 -31
  180. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +0 -1
  181. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +0 -15
  182. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +0 -18
  183. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  184. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +0 -6
  185. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  186. package/dist/esm/ts-rs/FormatUnit.d.ts +0 -1
  187. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +0 -1
  188. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +0 -1
  189. package/dist/esm/ts-rs/PluginConfig.d.ts +0 -2
  190. package/dist/esm/ts-rs/RoundingMode.d.ts +0 -1
  191. package/dist/esm/ts-rs/RoundingPriority.d.ts +0 -1
  192. package/dist/esm/ts-rs/SignDisplay.d.ts +0 -1
  193. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +0 -1
  194. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +0 -6
  195. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  196. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +0 -1
  197. package/dist/esm/ts-rs/UseGrouping.d.ts +0 -1
  198. package/src/rust/components/number_column_style.rs +0 -491
  199. package/src/rust/config/number_column_style.rs +0 -136
  200. package/src/rust/dragdrop.rs +0 -481
  201. package/src/rust/tasks/plugin_column_styles.rs +0 -98
  202. package/src/ts/ts-rs/ColumnConfigValues.ts +0 -14
  203. package/src/ts/ts-rs/CustomDatetimeFormat.ts +0 -3
  204. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +0 -5
  205. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +0 -8
  206. package/src/ts/ts-rs/DatetimeColorMode.ts +0 -3
  207. package/src/ts/ts-rs/DatetimeFormatType.ts +0 -8
  208. package/src/ts/ts-rs/FormatMode.ts +0 -3
  209. package/src/ts/ts-rs/NumberBackgroundMode.ts +0 -3
  210. package/src/ts/ts-rs/NumberForegroundMode.ts +0 -3
  211. package/src/ts/ts-rs/PluginConfig.ts +0 -4
  212. package/src/ts/ts-rs/RoundingMode.ts +0 -3
  213. package/src/ts/ts-rs/RoundingPriority.ts +0 -3
  214. package/src/ts/ts-rs/SignDisplay.ts +0 -3
  215. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +0 -3
  216. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +0 -4
  217. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  218. package/src/ts/ts-rs/TrailingZeroDisplay.ts +0 -3
  219. package/src/ts/ts-rs/UseGrouping.ts +0 -3
  220. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline0.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline1.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline2.js +0 -0
  223. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline3.js +0 -0
  224. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-3cd58f0374935772}/inline4.js +0 -0
  225. /package/src/rust/{tasks → config}/export_method.rs +0 -0
  226. /package/src/rust/{tasks → queries}/export_app.rs +0 -0
  227. /package/src/rust/{tasks → queries}/is_invalid_drop.rs +0 -0
@@ -19,7 +19,6 @@ use crate::js::*;
19
19
  use crate::presentation::Presentation;
20
20
  use crate::renderer::*;
21
21
  use crate::session::Session;
22
- use crate::tasks::*;
23
22
  use crate::utils::*;
24
23
  use crate::*;
25
24
 
@@ -77,30 +76,18 @@ struct IntersectionObserverState {
77
76
  presentation: Presentation,
78
77
  }
79
78
 
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
-
98
79
  impl IntersectionObserverState {
99
80
  async fn set_pause(self, intersect: bool) -> ApiResult<()> {
100
81
  if intersect {
101
82
  if self.session.set_pause(false) {
102
- self.restore_and_render(ViewerConfigUpdate::default(), async move { Ok(()) })
103
- .await?;
83
+ super::restore_and_render(
84
+ &self.session,
85
+ &self.renderer,
86
+ &self.presentation,
87
+ ViewerConfigUpdate::default(),
88
+ async move { Ok(()) },
89
+ )
90
+ .await?;
104
91
  }
105
92
  } else {
106
93
  self.session.set_pause(true);
@@ -10,43 +10,41 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- //! Async tasks and model traits for coordinating state between `Session`,
14
- //! `Renderer`, `Presentation`, and `DragDrop` singletons.
13
+ //! State-mutating async business logic dispatched from user actions.
15
14
  //!
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.
15
+ //! Every function in this module ends in side effects on one or more of
16
+ //! [`Session`], [`Renderer`], [`Presentation`] applying a
17
+ //! `ViewConfigUpdate`, drawing the active plugin, mutating expressions, etc.
18
+ //! Read-only async derivations belong in [`crate::queries`].
19
+ //!
20
+ //! [`Session`]: crate::session::Session
21
+ //! [`Renderer`]: crate::renderer::Renderer
22
+ //! [`Presentation`]: crate::presentation::Presentation
20
23
 
21
- mod column_locator;
22
- mod columns_iter_set;
23
24
  mod copy_export;
25
+ mod dismiss_render_warning;
24
26
  mod edit_expression;
25
27
  mod eject;
26
- mod export_app;
27
- mod export_method;
28
- mod get_viewer_config;
29
28
  mod intersection_observer;
30
- mod is_invalid_drop;
31
- mod plugin_column_styles;
29
+ mod reset_all;
32
30
  mod resize_observer;
33
31
  mod restore_and_render;
32
+ mod send_column_config;
34
33
  mod send_plugin_config;
35
- mod structural;
36
34
  mod update_and_render;
35
+ mod update_theme;
36
+ mod validate_expression;
37
37
 
38
- pub use self::column_locator::*;
39
- pub use self::columns_iter_set::*;
40
38
  pub use self::copy_export::*;
39
+ pub use self::dismiss_render_warning::*;
41
40
  pub use self::edit_expression::*;
42
41
  pub use self::eject::*;
43
- pub use self::export_method::*;
44
- pub use self::get_viewer_config::*;
45
42
  pub use self::intersection_observer::*;
46
- pub use self::is_invalid_drop::is_invalid_columns_column;
47
- pub use self::plugin_column_styles::*;
43
+ pub use self::reset_all::*;
48
44
  pub use self::resize_observer::*;
49
45
  pub use self::restore_and_render::*;
46
+ pub use self::send_column_config::*;
50
47
  pub use self::send_plugin_config::*;
51
- pub use self::structural::*;
52
48
  pub use self::update_and_render::*;
49
+ pub use self::update_theme::*;
50
+ pub use self::validate_expression::*;
@@ -0,0 +1,98 @@
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
+ //! Cross-engine reset orchestration: reset session config, optionally clear
14
+ //! presentation columns config / theme, then delegate to `restore_and_render`
15
+ //! to switch back to the default plugin and redraw.
16
+
17
+ use futures::channel::oneshot;
18
+ use perspective_client::clone;
19
+ use perspective_js::utils::ApiFuture;
20
+
21
+ use super::restore_and_render;
22
+ use crate::config::{
23
+ ColumnConfigUpdate, OptionalUpdate, PluginConfigUpdate, PluginUpdate, ViewerConfigUpdate,
24
+ };
25
+ use crate::presentation::Presentation;
26
+ use crate::renderer::Renderer;
27
+ use crate::session::{ResetOptions, Session};
28
+
29
+ /// Reset the viewer's `ViewerConfig` to the default.
30
+ ///
31
+ /// - `all = false`: clears the view config but preserves expressions and
32
+ /// per-column style maps.
33
+ /// - `all = true`: also clears expressions, per-column styles, and theme.
34
+ ///
35
+ /// Optionally signals `sender` once the reset+redraw round-trip completes,
36
+ /// then emits `renderer.reset_changed`.
37
+ ///
38
+ /// Delegates plugin selection + draw to [`restore_and_render`], whose
39
+ /// two-pass restore guarantees the default plugin sees materialized
40
+ /// `columns_config` / `plugin_config` on its first draw — fixing a race
41
+ /// where the raw post-reset bucket would reach the plugin before
42
+ /// stats-dependent `include: true` defaults were resolved.
43
+ pub fn reset_all(
44
+ session: &Session,
45
+ renderer: &Renderer,
46
+ presentation: &Presentation,
47
+ all: bool,
48
+ sender: Option<oneshot::Sender<()>>,
49
+ ) {
50
+ presentation.set_open_column_settings(None);
51
+ clone!(session, renderer, presentation);
52
+ ApiFuture::spawn(async move {
53
+ session
54
+ .reset(ResetOptions {
55
+ config: true,
56
+ expressions: all,
57
+ ..ResetOptions::default()
58
+ })
59
+ .await?;
60
+
61
+ presentation.reset_available_themes(None).await;
62
+ if all {
63
+ presentation.reset_theme().await?;
64
+ }
65
+
66
+ // For `all = true`, route the bucket clears through `restore_and_render`'s
67
+ // `update_*` paths as `SetDefault`. This guarantees the materialized
68
+ // restore fires even when the user is already on the default plugin
69
+ // (no plugin_swap signal), since `SetDefault` reports the bucket as
70
+ // `changed` when it was non-empty. The per-plugin bucket model means
71
+ // only the (post-swap) default plugin's bucket is cleared; other
72
+ // plugins' buckets persist with their per-plugin state.
73
+ let (columns_config, plugin_config) = if all {
74
+ (
75
+ ColumnConfigUpdate::SetDefault,
76
+ PluginConfigUpdate::SetDefault,
77
+ )
78
+ } else {
79
+ (OptionalUpdate::Missing, OptionalUpdate::Missing)
80
+ };
81
+
82
+ let update = ViewerConfigUpdate {
83
+ plugin: PluginUpdate::SetDefault,
84
+ plugin_config,
85
+ columns_config,
86
+ ..Default::default()
87
+ };
88
+
89
+ restore_and_render(&session, &renderer, &presentation, update, async { Ok(()) }).await?;
90
+
91
+ if let Some(sender) = sender {
92
+ sender.send(()).unwrap();
93
+ }
94
+
95
+ renderer.reset_changed.emit(());
96
+ Ok(())
97
+ })
98
+ }
@@ -22,7 +22,6 @@ use crate::presentation::Presentation;
22
22
  use crate::renderer::*;
23
23
  use crate::root::Root;
24
24
  use crate::session::{Session, TableLoadState};
25
- use crate::tasks::*;
26
25
  use crate::utils::*;
27
26
 
28
27
  pub struct ResizeObserverHandle {
@@ -83,32 +82,6 @@ struct ResizeObserverState {
83
82
  on_resize: Callback<()>,
84
83
  }
85
84
 
86
- impl HasRenderer for ResizeObserverState {
87
- fn renderer(&self) -> &Renderer {
88
- &self.renderer
89
- }
90
- }
91
-
92
- impl HasSession for ResizeObserverState {
93
- fn session(&self) -> &Session {
94
- &self.session
95
- }
96
- }
97
-
98
- impl HasPresentation for ResizeObserverState {
99
- fn presentation(&self) -> &'_ crate::presentation::Presentation {
100
- &self.presentation
101
- }
102
- }
103
-
104
- impl StateProvider for ResizeObserverState {
105
- type State = ResizeObserverState;
106
-
107
- fn clone_state(&self) -> Self::State {
108
- self.clone()
109
- }
110
- }
111
-
112
85
  impl ResizeObserverState {
113
86
  fn on_resize(&mut self, entries: &js_sys::Array) {
114
87
  let is_visible = self
@@ -124,16 +97,16 @@ impl ResizeObserverState {
124
97
  let content_height = content.height().floor() as i32;
125
98
  let resized = self.width != content_width || self.height != content_height;
126
99
  if resized && is_visible {
127
- let state = self.clone_state();
100
+ let state = self.clone();
128
101
  clone!(self.on_resize);
129
102
  ApiFuture::spawn_throttled(async move {
130
103
  let needs_render = state
131
- .renderer()
104
+ .renderer
132
105
  .clone()
133
106
  .with_lock(async {
134
- Ok(!state.renderer().is_plugin_activated()?
107
+ Ok(!state.renderer.is_plugin_activated()?
135
108
  && matches!(
136
- state.session().has_table(),
109
+ state.session.has_table(),
137
110
  Some(TableLoadState::Loaded)
138
111
  ))
139
112
  })
@@ -141,9 +114,14 @@ impl ResizeObserverState {
141
114
 
142
115
  if needs_render {
143
116
  state.presentation.reset_attached();
144
- state.update_and_render(Default::default())?.await?;
117
+ super::update_and_render(
118
+ &state.session,
119
+ &state.renderer,
120
+ Default::default(),
121
+ )?
122
+ .await?;
145
123
  } else {
146
- state.renderer().resize().await?;
124
+ state.renderer.resize().await?;
147
125
  }
148
126
 
149
127
  on_resize.emit(());
@@ -10,110 +10,148 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ //! Apply a full [`ViewerConfigUpdate`] (settings, theme, title, plugin,
14
+ //! plugin_config, columns_config, view_config) and re-draw.
15
+
13
16
  use futures::Future;
17
+ use perspective_client::clone;
14
18
 
15
- use super::structural::*;
16
19
  use crate::config::{OptionalUpdate, ViewerConfigUpdate};
17
- use crate::utils::*;
20
+ use crate::presentation::Presentation;
21
+ use crate::renderer::Renderer;
22
+ use crate::session::Session;
18
23
  use crate::*;
19
24
 
20
- pub trait RestoreAndRender: HasRenderer + HasSession + HasPresentation {
21
- /// Apply a `ViewConfigUpdate` to the current `View` and render.
22
- fn restore_and_render(
23
- &self,
24
- ViewerConfigUpdate {
25
- plugin,
26
- plugin_config,
27
- columns_config,
28
- settings,
29
- theme: theme_name,
30
- title,
31
- mut view_config,
32
- ..
33
- }: crate::config::ViewerConfigUpdate,
34
- task: impl Future<Output = Result<(), ApiError>> + 'static,
35
- ) -> ApiFuture<()> {
36
- clone!(self.session(), self.renderer(), self.presentation());
37
- ApiFuture::new(async move {
38
- if let OptionalUpdate::Update(x) = settings {
39
- presentation.set_settings_attribute(x);
40
- presentation.set_settings_before_open(x);
41
- }
25
+ /// Apply a full [`ViewerConfigUpdate`] (theme, title, plugin selection,
26
+ /// plugin config, columns config, view config) to the engines and re-draw.
27
+ /// Returns an [`ApiFuture<()>`] which resolves when the draw completes.
28
+ pub fn restore_and_render(
29
+ session: &Session,
30
+ renderer: &Renderer,
31
+ presentation: &Presentation,
32
+ ViewerConfigUpdate {
33
+ plugin,
34
+ plugin_config,
35
+ columns_config,
36
+ settings,
37
+ theme: theme_name,
38
+ title,
39
+ mut view_config,
40
+ ..
41
+ }: ViewerConfigUpdate,
42
+ task: impl Future<Output = Result<(), ApiError>> + 'static,
43
+ ) -> ApiFuture<()> {
44
+ clone!(session, renderer, presentation);
45
+ ApiFuture::new(async move {
46
+ if let OptionalUpdate::Update(x) = settings {
47
+ presentation.set_settings_attribute(x);
48
+ presentation.set_settings_before_open(x);
49
+ }
42
50
 
43
- if let OptionalUpdate::Update(title) = title {
44
- session.set_title(Some(title));
45
- } else if matches!(title, OptionalUpdate::SetDefault) {
46
- session.set_title(None);
47
- }
51
+ if let OptionalUpdate::Update(title) = title {
52
+ session.set_title(Some(title));
53
+ } else if matches!(title, OptionalUpdate::SetDefault) {
54
+ session.set_title(None);
55
+ }
56
+
57
+ let needs_restyle = match theme_name {
58
+ OptionalUpdate::SetDefault => {
59
+ let current_name = presentation.get_selected_theme_name().await;
60
+ if current_name.is_some() {
61
+ presentation.set_theme_name(None).await?;
62
+ true
63
+ } else {
64
+ false
65
+ }
66
+ },
67
+ OptionalUpdate::Update(x) => {
68
+ let current_name = presentation.get_selected_theme_name().await;
69
+ if current_name.is_some() && current_name.as_ref().unwrap() != &x {
70
+ presentation.set_theme_name(Some(&x)).await?;
71
+ true
72
+ } else {
73
+ false
74
+ }
75
+ },
76
+ _ => false,
77
+ };
78
+
79
+ if let Some(metadata) = renderer.get_next_plugin_metadata(&plugin) {
80
+ session.set_update_column_defaults(&mut view_config, &metadata);
81
+ }
82
+
83
+ session.update_view_config(view_config)?;
84
+ let draw_task = renderer.draw(async {
85
+ task.await?;
86
+ let plugin_swapped = renderer.apply_pending_plugin()?;
87
+ let plugin = renderer.get_active_plugin()?;
48
88
 
49
- let needs_restyle = match theme_name {
50
- OptionalUpdate::SetDefault => {
51
- let current_name = presentation.get_selected_theme_name().await;
52
- if current_name.is_some() {
53
- presentation.set_theme_name(None).await?;
54
- true
55
- } else {
56
- false
57
- }
58
- },
59
- OptionalUpdate::Update(x) => {
60
- let current_name = presentation.get_selected_theme_name().await;
61
- if current_name.is_some() && current_name.as_ref().unwrap() != &x {
62
- presentation.set_theme_name(Some(&x)).await?;
63
- true
64
- } else {
65
- false
66
- }
67
- },
68
- _ => false,
69
- };
70
-
71
- if let Some(metadata) = renderer.get_next_plugin_metadata(&plugin) {
72
- session.set_update_column_defaults(&mut view_config, &metadata);
89
+ // The previous call which acquired the lock errored, so skip this render
90
+ if let Some(error) = session.get_error() {
91
+ return Err(error);
73
92
  }
74
93
 
75
- session.update_view_config(view_config)?;
76
- let draw_task = renderer.draw(async {
77
- task.await?;
78
- renderer.apply_pending_plugin()?;
79
- let plugin = renderer.get_active_plugin()?;
80
- let plugin_update = if let Some(x) = plugin_config {
81
- wasm_bindgen::JsValue::from_serde_ext(&*x).unwrap()
82
- } else {
83
- plugin.save()?
84
- };
94
+ // Validate + create the view BEFORE applying
95
+ // columns_config / plugin_config updates, so the
96
+ // strip-on-write and materialize passes see fresh
97
+ // `expression_schema` and `view_schema`, and the
98
+ // materialize warm step can call `View::get_min_max`
99
+ // against a view that knows about any new expression
100
+ // columns. Previously this happened after the strip,
101
+ // which silently dropped any `columns_config` entry
102
+ // keyed by a new expression column.
103
+ let view = session.validate().await?.create_view().await?;
85
104
 
86
- presentation.update_columns_configs(columns_config);
87
- let columns_config = presentation.all_columns_configs();
88
- plugin.restore(&plugin_update, Some(&columns_config))?;
105
+ // Apply incoming updates into the now-active plugin's
106
+ // bucket on `Renderer`. Per-plugin storage means no
107
+ // schema filter is needed before restore — foreign keys
108
+ // cannot appear in the bucket by construction. The
109
+ // renderer methods also strip schema-default entries so
110
+ // restored maps containing default values produce an
111
+ // empty bucket (correct: bucket-empty ⇒ reads-default).
112
+ let view_config_snapshot = session.get_view_config().clone();
113
+ let plugin_config_changed =
114
+ renderer.update_plugin_config(&view_config_snapshot, plugin_config);
115
+ let columns_config_changed =
116
+ renderer.update_columns_configs(&view_config_snapshot, &session, columns_config);
117
+ let changed = plugin_config_changed || columns_config_changed;
89
118
 
90
- // The previous call which acquired the lock errored, so skip this render
91
- if let Some(error) = session.get_error() {
92
- return Err(error);
119
+ // Force a materialized restore when the plugin just
120
+ // swapped `commit_plugin_idx` already restored from the
121
+ // raw bucket, but the materialized restore is needed for
122
+ // schema-revealed `include: true` defaults to reach the
123
+ // plugin before its first draw.
124
+ if changed || plugin_swapped {
125
+ let plugin_config_snapshot = renderer.get_plugin_config();
126
+ let plugin_update =
127
+ wasm_bindgen::JsValue::from_serde_ext(&plugin_config_snapshot).unwrap();
128
+ let columns_config = renderer
129
+ .all_columns_configs_materialized(&view_config_snapshot, &session)
130
+ .await;
131
+ plugin.restore(&plugin_update, Some(&columns_config))?;
132
+ if plugin_config_changed {
133
+ renderer.plugin_config_changed.emit(plugin_config_snapshot);
93
134
  }
135
+ }
94
136
 
95
- let view = session.validate().await?.create_view().await;
96
- if !presentation.is_visible() {
97
- Ok(None)
98
- } else {
99
- view
100
- }
101
- });
137
+ if !presentation.is_visible() {
138
+ Ok(None)
139
+ } else {
140
+ Ok(view)
141
+ }
142
+ });
102
143
 
103
- draw_task.await?;
144
+ draw_task.await?;
104
145
 
105
- // TODO this should be part of the API for `draw()` above, such that
106
- // the plugin need not render twice when a theme is provided.
107
- if needs_restyle
108
- && presentation.is_visible()
109
- && let Some(view) = session.get_view()
110
- {
111
- renderer.restyle_all(&view).await?;
112
- }
146
+ // TODO this should be part of the API for `draw()` above, such that
147
+ // the plugin need not render twice when a theme is provided.
148
+ if needs_restyle
149
+ && presentation.is_visible()
150
+ && let Some(view) = session.get_view()
151
+ {
152
+ renderer.restyle_all(&view).await?;
153
+ }
113
154
 
114
- Ok(())
115
- })
116
- }
155
+ Ok(())
156
+ })
117
157
  }
118
-
119
- impl<T: HasRenderer + HasSession + HasPresentation> RestoreAndRender for T {}
@@ -10,41 +10,45 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use super::structural::*;
14
- use crate::config::*;
15
- use crate::*;
13
+ use perspective_client::clone;
14
+ use perspective_js::utils::*;
16
15
 
17
- /// A `ViewerConfig` is constructed from various properties acrosss the
18
- /// application state
16
+ use crate::config::ColumnConfigFieldUpdate;
17
+ use crate::renderer::Renderer;
18
+ use crate::session::Session;
19
+
20
+ /// Apply a [`ColumnConfigFieldUpdate`] from a column-style sidebar
21
+ /// control to the active plugin's per-column config bucket on
22
+ /// [`Renderer`], then re-`restore` the active plugin with the
23
+ /// updated maps and trigger a render.
19
24
  ///
20
- /// For example, the current `Plugin`, `ViewConfig`, and `Theme`.
21
- /// `GetViewerConfigModel` provides methods which should be used to get the
22
- /// applications `ViewerConfig` from across these state objects.
23
- pub trait GetViewerConfigModel: HasSession + HasRenderer + HasPresentation {
24
- /// Get the current [`ViewerConfig`]`
25
- async fn get_viewer_config(&self) -> ApiResult<ViewerConfig> {
26
- let version = config::API_VERSION.to_string();
27
- let view_config = self.session().get_view_config().clone();
28
- let js_plugin = self.renderer().get_active_plugin()?;
29
- let settings = self.presentation().is_settings_open();
30
- let plugin = js_plugin.name();
31
- let plugin_config: serde_json::Value = js_plugin.save()?.into_serde_ext()?;
32
- let theme = self.presentation().get_selected_theme_name().await;
33
- let title = self.session().get_title();
34
- let table = self.session().get_table().map(|x| x.get_name().to_owned());
35
- let columns_config = self.presentation().all_columns_configs();
36
- Ok(ViewerConfig {
37
- version,
38
- plugin,
39
- title,
40
- plugin_config,
41
- columns_config,
42
- settings,
43
- table,
44
- view_config,
45
- theme,
46
- })
47
- }
48
- }
25
+ /// Plugin-level (non-column) field updates go through
26
+ /// [`super::send_plugin_config`] instead.
27
+ pub fn send_column_config(
28
+ session: &Session,
29
+ renderer: &Renderer,
30
+ column_name: &str,
31
+ update: ColumnConfigFieldUpdate,
32
+ ) {
33
+ // Apply the renderer write synchronously so the StyleTab's
34
+ // revision-bump re-render path sees the new state immediately.
35
+ let view_config = session.get_view_config().clone();
36
+ renderer.update_columns_config_field(&view_config, session, column_name.to_string(), update);
49
37
 
50
- impl<T: HasRenderer + HasSession + HasPresentation> GetViewerConfigModel for T {}
38
+ clone!(session, renderer);
39
+ ApiFuture::spawn(async move {
40
+ let view_config_snapshot = session.get_view_config().clone();
41
+ let columns_configs = renderer
42
+ .all_columns_configs_materialized(&view_config_snapshot, &session)
43
+ .await;
44
+ let plugin_token =
45
+ wasm_bindgen::JsValue::from_serde_ext(&renderer.get_plugin_config()).unwrap();
46
+ renderer
47
+ .get_active_plugin()?
48
+ .restore(&plugin_token, Some(&columns_configs))?;
49
+
50
+ renderer.update(session.get_view()).await?;
51
+ renderer.column_style_changed.emit(columns_configs);
52
+ Ok(())
53
+ })
54
+ }