@perspective-dev/viewer 4.4.1 → 4.5.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 (225) 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 +293 -144
  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 +35 -6
  44. package/src/css/column-style.css +27 -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 +0 -4
  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 +65 -3
  53. package/src/rust/components/column_dropdown.rs +3 -1
  54. package/src/rust/components/column_selector/active_column.rs +13 -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 +9 -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 +394 -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 +43 -49
  66. package/src/rust/components/containers/dragdrop_list.rs +5 -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 +2 -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 +140 -173
  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 +92 -131
  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 +640 -51
  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 +78 -0
  136. package/src/rust/tasks/resize_observer.rs +11 -33
  137. package/src/rust/tasks/restore_and_render.rs +117 -90
  138. package/src/rust/tasks/{get_viewer_config.rs → send_column_config.rs} +38 -35
  139. package/src/rust/tasks/send_plugin_config.rs +32 -33
  140. package/src/rust/tasks/update_and_render.rs +66 -47
  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/mega-menu-icons-density.svg +23 -0
  146. package/src/svg/mega-menu-icons-map-density.svg +24 -0
  147. package/src/svg/mega-menu-icons-map-line.svg +19 -0
  148. package/src/themes/botanical.css +27 -53
  149. package/src/themes/defaults.css +24 -36
  150. package/src/themes/dracula.css +36 -54
  151. package/src/themes/gruvbox-dark.css +39 -59
  152. package/src/themes/gruvbox.css +16 -28
  153. package/src/themes/icons.css +3 -0
  154. package/src/themes/intl/de.css +42 -6
  155. package/src/themes/intl/es.css +42 -6
  156. package/src/themes/intl/fr.css +42 -6
  157. package/src/themes/intl/ja.css +42 -6
  158. package/src/themes/intl/pt.css +42 -6
  159. package/src/themes/intl/zh.css +42 -6
  160. package/src/themes/intl.css +37 -4
  161. package/src/themes/monokai.css +45 -61
  162. package/src/themes/phosphor.css +20 -29
  163. package/src/themes/pro-dark.css +25 -34
  164. package/src/themes/solarized-dark.css +21 -36
  165. package/src/themes/solarized.css +13 -23
  166. package/src/themes/vaporwave.css +40 -74
  167. package/src/ts/bootstrap.ts +14 -3
  168. package/src/ts/column-format.ts +162 -0
  169. package/src/ts/extensions.ts +4 -0
  170. package/src/ts/perspective-viewer.ts +9 -1
  171. package/src/{rust/components/column_settings_sidebar/style_tab/stub.rs → ts/perspective-viewer.worker.ts} +2 -22
  172. package/src/ts/plugin.ts +25 -101
  173. package/src/ts/ts-rs/{FormatUnit.ts → ColumnSelectMode.ts} +1 -1
  174. package/src/ts/ts-rs/PluginStaticConfig.ts +78 -0
  175. package/src/ts/ts-rs/ViewerConfig.ts +1 -2
  176. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -3
  177. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +0 -31
  178. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +0 -1
  179. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +0 -15
  180. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +0 -18
  181. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  182. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +0 -6
  183. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  184. package/dist/esm/ts-rs/FormatUnit.d.ts +0 -1
  185. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +0 -1
  186. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +0 -1
  187. package/dist/esm/ts-rs/PluginConfig.d.ts +0 -2
  188. package/dist/esm/ts-rs/RoundingMode.d.ts +0 -1
  189. package/dist/esm/ts-rs/RoundingPriority.d.ts +0 -1
  190. package/dist/esm/ts-rs/SignDisplay.d.ts +0 -1
  191. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +0 -1
  192. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +0 -6
  193. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  194. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +0 -1
  195. package/dist/esm/ts-rs/UseGrouping.d.ts +0 -1
  196. package/src/rust/components/number_column_style.rs +0 -491
  197. package/src/rust/config/number_column_style.rs +0 -136
  198. package/src/rust/dragdrop.rs +0 -481
  199. package/src/rust/tasks/plugin_column_styles.rs +0 -98
  200. package/src/ts/ts-rs/ColumnConfigValues.ts +0 -14
  201. package/src/ts/ts-rs/CustomDatetimeFormat.ts +0 -3
  202. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +0 -5
  203. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +0 -8
  204. package/src/ts/ts-rs/DatetimeColorMode.ts +0 -3
  205. package/src/ts/ts-rs/DatetimeFormatType.ts +0 -8
  206. package/src/ts/ts-rs/FormatMode.ts +0 -3
  207. package/src/ts/ts-rs/NumberBackgroundMode.ts +0 -3
  208. package/src/ts/ts-rs/NumberForegroundMode.ts +0 -3
  209. package/src/ts/ts-rs/PluginConfig.ts +0 -4
  210. package/src/ts/ts-rs/RoundingMode.ts +0 -3
  211. package/src/ts/ts-rs/RoundingPriority.ts +0 -3
  212. package/src/ts/ts-rs/SignDisplay.ts +0 -3
  213. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +0 -3
  214. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +0 -4
  215. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  216. package/src/ts/ts-rs/TrailingZeroDisplay.ts +0 -3
  217. package/src/ts/ts-rs/UseGrouping.ts +0 -3
  218. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline0.js +0 -0
  219. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline1.js +0 -0
  220. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline2.js +0 -0
  221. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline3.js +0 -0
  222. /package/dist/wasm/snippets/{perspective-viewer-d924246f0b4a3dce → perspective-viewer-39ab7da3ca157861}/inline4.js +0 -0
  223. /package/src/rust/{tasks → config}/export_method.rs +0 -0
  224. /package/src/rust/{tasks → queries}/export_app.rs +0 -0
  225. /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,78 @@
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, reset the renderer plugin state, and
15
+ //! redraw.
16
+
17
+ use futures::channel::oneshot;
18
+ use perspective_client::clone;
19
+ use perspective_js::utils::ApiFuture;
20
+
21
+ use crate::presentation::Presentation;
22
+ use crate::renderer::Renderer;
23
+ use crate::session::{ResetOptions, Session};
24
+
25
+ /// Reset the viewer's `ViewerConfig` to the default.
26
+ ///
27
+ /// - `all = false`: clears the view config but preserves expressions and
28
+ /// per-column style maps.
29
+ /// - `all = true`: also clears expressions, per-column styles, and theme.
30
+ ///
31
+ /// Optionally signals `sender` once the reset+redraw round-trip completes,
32
+ /// then emits `renderer.reset_changed`.
33
+ pub fn reset_all(
34
+ session: &Session,
35
+ renderer: &Renderer,
36
+ presentation: &Presentation,
37
+ all: bool,
38
+ sender: Option<oneshot::Sender<()>>,
39
+ ) {
40
+ presentation.set_open_column_settings(None);
41
+ clone!(session, renderer, presentation);
42
+ ApiFuture::spawn(async move {
43
+ session
44
+ .reset(ResetOptions {
45
+ config: true,
46
+ expressions: all,
47
+ ..ResetOptions::default()
48
+ })
49
+ .await?;
50
+ let columns_config = if all {
51
+ renderer.reset_columns_configs();
52
+ renderer.reset_plugin_config();
53
+ // Mirror the per-plugin bucket clear on the event bus so
54
+ // `PluginTab` re-pulls (its props are interior-mutable
55
+ // handles whose identity doesn't change on the reset).
56
+ renderer
57
+ .plugin_config_changed
58
+ .emit(renderer.get_plugin_config());
59
+ None
60
+ } else {
61
+ Some(renderer.all_columns_configs())
62
+ };
63
+
64
+ renderer.reset(columns_config.as_ref()).await?;
65
+ presentation.reset_available_themes(None).await;
66
+ if all {
67
+ presentation.reset_theme().await?;
68
+ }
69
+
70
+ let result = renderer.draw(session.validate().await?.create_view()).await;
71
+ if let Some(sender) = sender {
72
+ sender.send(()).unwrap();
73
+ }
74
+
75
+ renderer.reset_changed.emit(());
76
+ result
77
+ })
78
+ }
@@ -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,137 @@
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
- }
48
-
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);
73
- }
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
+ }
74
56
 
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()
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
82
63
  } else {
83
- plugin.save()?
84
- };
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
+ };
85
78
 
86
- presentation.update_columns_configs(columns_config);
87
- let columns_config = presentation.all_columns_configs();
88
- plugin.restore(&plugin_update, Some(&columns_config))?;
79
+ if let Some(metadata) = renderer.get_next_plugin_metadata(&plugin) {
80
+ session.set_update_column_defaults(&mut view_config, &metadata);
81
+ }
89
82
 
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);
93
- }
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()?;
94
88
 
95
- let view = session.validate().await?.create_view().await;
96
- if !presentation.is_visible() {
97
- Ok(None)
98
- } else {
99
- view
89
+ // Apply incoming updates into the now-active plugin's
90
+ // bucket on `Renderer`. Per-plugin storage means no
91
+ // schema filter is needed before restore — foreign keys
92
+ // cannot appear in the bucket by construction. The
93
+ // renderer methods also strip schema-default entries so
94
+ // restored maps containing default values produce an
95
+ // empty bucket (correct: bucket-empty ⇒ reads-default).
96
+ let view_config_snapshot = session.get_view_config().clone();
97
+ let plugin_config_changed =
98
+ renderer.update_plugin_config(&view_config_snapshot, plugin_config);
99
+
100
+ let changed = plugin_config_changed
101
+ || renderer.update_columns_configs(&view_config_snapshot, &session, columns_config);
102
+
103
+ // Force a materialized restore when the plugin just
104
+ // swapped — `commit_plugin_idx` already restored from the
105
+ // raw bucket, but the materialized restore is needed for
106
+ // schema-revealed `include: true` defaults to reach the
107
+ // plugin before its first draw.
108
+ if changed || plugin_swapped {
109
+ let plugin_config_snapshot = renderer.get_plugin_config();
110
+ let plugin_update =
111
+ wasm_bindgen::JsValue::from_serde_ext(&plugin_config_snapshot).unwrap();
112
+ let columns_config =
113
+ renderer.all_columns_configs_materialized(&view_config_snapshot, &session);
114
+ plugin.restore(&plugin_update, Some(&columns_config))?;
115
+ if plugin_config_changed {
116
+ renderer.plugin_config_changed.emit(plugin_config_snapshot);
100
117
  }
101
- });
118
+ }
102
119
 
103
- draw_task.await?;
120
+ // The previous call which acquired the lock errored, so skip this render
121
+ if let Some(error) = session.get_error() {
122
+ return Err(error);
123
+ }
104
124
 
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?;
125
+ let view = session.validate().await?.create_view().await;
126
+ if !presentation.is_visible() {
127
+ Ok(None)
128
+ } else {
129
+ view
112
130
  }
131
+ });
113
132
 
114
- Ok(())
115
- })
116
- }
117
- }
133
+ draw_task.await?;
118
134
 
119
- impl<T: HasRenderer + HasSession + HasPresentation> RestoreAndRender for T {}
135
+ // TODO this should be part of the API for `draw()` above, such that
136
+ // the plugin need not render twice when a theme is provided.
137
+ if needs_restyle
138
+ && presentation.is_visible()
139
+ && let Some(view) = session.get_view()
140
+ {
141
+ renderer.restyle_all(&view).await?;
142
+ }
143
+
144
+ Ok(())
145
+ })
146
+ }
@@ -10,41 +10,44 @@
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 =
42
+ renderer.all_columns_configs_materialized(&view_config_snapshot, &session);
43
+ let plugin_token =
44
+ wasm_bindgen::JsValue::from_serde_ext(&renderer.get_plugin_config()).unwrap();
45
+ renderer
46
+ .get_active_plugin()?
47
+ .restore(&plugin_token, Some(&columns_configs))?;
48
+
49
+ renderer.update(session.get_view()).await?;
50
+ renderer.column_style_changed.emit(columns_configs);
51
+ Ok(())
52
+ })
53
+ }
@@ -10,42 +10,41 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ use perspective_client::clone;
13
14
  use perspective_js::utils::*;
14
15
 
15
- use crate::config::ColumnConfigValueUpdate;
16
- use crate::tasks::*;
16
+ use crate::config::ColumnConfigFieldUpdate;
17
+ use crate::renderer::Renderer;
18
+ use crate::session::Session;
17
19
 
18
- pub trait SendPluginConfig {
19
- /// Update te urrent plugin with a [`ColumnonfigValueUpdate`]
20
- fn send_plugin_config(&self, column_name: &str, update: ColumnConfigValueUpdate);
21
- }
22
-
23
- impl<A> SendPluginConfig for A
24
- where
25
- A: Clone + HasCustomEvents + HasPresentation + HasRenderer + HasSession + 'static,
26
- {
27
- fn send_plugin_config(&self, column_name: &str, update: ColumnConfigValueUpdate) {
28
- let name = column_name.to_string();
29
- let props = self.clone();
30
- ApiFuture::spawn(async move {
31
- props
32
- .presentation()
33
- .update_columns_config_value(name.clone(), update);
34
-
35
- let columns_configs = props.presentation().all_columns_configs();
36
- let plugin_config = props.renderer().get_active_plugin()?.save()?;
37
- props
38
- .renderer()
20
+ /// Apply a [`ColumnConfigFieldUpdate`] from the Plugin-settings tab to
21
+ /// the active plugin's bucket on [`Renderer`], then re-`restore` the
22
+ /// plugin with the merged token and trigger a render.
23
+ ///
24
+ /// Per-plugin buckets mean no schema filter is needed before restore —
25
+ /// keys from a different plugin physically cannot appear in this
26
+ /// plugin's bucket. Schema-default stripping is handled inside
27
+ /// [`Renderer::update_plugin_config_field`].
28
+ ///
29
+ /// Column-style updates go through [`super::send_column_config`].
30
+ pub fn send_plugin_config(session: &Session, renderer: &Renderer, update: ColumnConfigFieldUpdate) {
31
+ let view_config = session.get_view_config().clone();
32
+ let changed = renderer.update_plugin_config_field(&view_config, update);
33
+ clone!(session, renderer);
34
+ ApiFuture::spawn(async move {
35
+ if changed {
36
+ let plugin_config = renderer.get_plugin_config();
37
+ let plugin_token = wasm_bindgen::JsValue::from_serde_ext(&plugin_config).unwrap();
38
+ let view_config_snapshot = session.get_view_config().clone();
39
+ let columns_configs =
40
+ renderer.all_columns_configs_materialized(&view_config_snapshot, &session);
41
+ renderer
39
42
  .get_active_plugin()?
40
- .restore(&plugin_config, Some(&columns_configs))?;
41
-
42
- props.renderer().update(props.session().get_view()).await?;
43
- let detail = serde_wasm_bindgen::to_value(&columns_configs).unwrap();
44
- props
45
- .custom_events()
46
- .dispatch_column_style_changed(&detail)?;
43
+ .restore(&plugin_token, Some(&columns_configs))?;
44
+ renderer.update(session.get_view()).await?;
45
+ renderer.plugin_config_changed.emit(plugin_config);
46
+ }
47
47
 
48
- Ok(())
49
- })
50
- }
48
+ Ok(())
49
+ })
51
50
  }