@perspective-dev/viewer 4.5.1 → 5.0.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 (212) hide show
  1. package/dist/cdn/perspective-viewer.js +20 -1
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/monokai.css +1 -1
  9. package/dist/css/phosphor.css +1 -1
  10. package/dist/css/pro-dark.css +1 -1
  11. package/dist/css/pro.css +1 -1
  12. package/dist/css/solarized-dark.css +1 -1
  13. package/dist/css/solarized.css +1 -1
  14. package/dist/css/themes.css +1 -1
  15. package/dist/css/vaporwave.css +1 -1
  16. package/dist/esm/extensions.d.ts +22 -3
  17. package/dist/esm/perspective-viewer.cdn.d.ts +3 -0
  18. package/dist/esm/perspective-viewer.d.ts +9 -0
  19. package/dist/esm/perspective-viewer.inline.d.ts +3 -0
  20. package/dist/esm/perspective-viewer.inline.js +20 -1
  21. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  22. package/dist/esm/perspective-viewer.js +20 -1
  23. package/dist/esm/perspective-viewer.js.map +4 -4
  24. package/dist/esm/plugin.d.ts +71 -5
  25. package/dist/esm/ts-rs/ClientOptions.d.ts +7 -0
  26. package/dist/esm/ts-rs/ExportMethod.d.ts +1 -0
  27. package/dist/esm/ts-rs/ExportOptions.d.ts +9 -0
  28. package/dist/esm/ts-rs/GetClientOptions.d.ts +8 -0
  29. package/dist/esm/ts-rs/GetTableOptions.d.ts +8 -0
  30. package/dist/esm/ts-rs/Layout.d.ts +11 -0
  31. package/dist/esm/ts-rs/OnUpdateData.d.ts +6 -0
  32. package/dist/esm/ts-rs/Orientation.d.ts +4 -0
  33. package/dist/esm/ts-rs/PanelOptions.d.ts +7 -0
  34. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +39 -0
  35. package/dist/esm/ts-rs/SplitLayout.d.ts +7 -0
  36. package/dist/esm/ts-rs/TabLayout.d.ts +10 -0
  37. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +4 -0
  38. package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -2
  39. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +40 -0
  40. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +28 -0
  41. package/dist/wasm/perspective-viewer.d.ts +213 -79
  42. package/dist/wasm/perspective-viewer.js +537 -189
  43. package/dist/wasm/perspective-viewer.wasm +0 -0
  44. package/dist/wasm/perspective-viewer.wasm.d.ts +36 -28
  45. package/dist/wasm/snippets/perspective-viewer-daeef4ceb066e6c7/inline0.js +6 -0
  46. package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline3.js → perspective-viewer-daeef4ceb066e6c7/inline4.js} +5 -0
  47. package/package.json +3 -2
  48. package/src/css/_column_dropdown.css +14 -0
  49. package/src/css/_context_menu.css +14 -0
  50. package/src/css/_dropdown_menu.css +14 -0
  51. package/src/css/_filter_dropdown.css +14 -0
  52. package/src/css/_function_dropdown.css +14 -0
  53. package/src/css/_viewer_bundle.css +41 -0
  54. package/src/css/column-settings-panel.css +1 -1
  55. package/src/css/containers/context-menu.css +101 -0
  56. package/src/css/panel-tab.css +193 -0
  57. package/src/css/plugin-selector.css +7 -10
  58. package/src/css/status-bar.css +75 -81
  59. package/src/css/viewer.css +181 -71
  60. package/src/rust/components/column_dropdown.rs +43 -33
  61. package/src/rust/components/column_selector/active_column.rs +7 -18
  62. package/src/rust/components/column_selector/aggregate_selector.rs +4 -10
  63. package/src/rust/components/column_selector/config_selector.rs +33 -84
  64. package/src/rust/components/column_selector/empty_column.rs +0 -3
  65. package/src/rust/components/column_selector/filter_column.rs +5 -18
  66. package/src/rust/components/column_selector/inactive_column.rs +3 -8
  67. package/src/rust/components/column_selector/invalid_column.rs +0 -4
  68. package/src/rust/components/column_selector/sort_column.rs +3 -6
  69. package/src/rust/components/column_selector.rs +5 -14
  70. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +0 -3
  71. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +0 -3
  72. package/src/rust/components/column_settings_sidebar.rs +0 -3
  73. package/src/rust/components/containers/dropdown_menu.rs +1 -3
  74. package/src/rust/components/containers/scroll_panel.rs +1 -3
  75. package/src/rust/components/containers/split_panel.rs +175 -66
  76. package/src/rust/components/containers/tab_list.rs +0 -3
  77. package/src/rust/components/context_menu.rs +130 -0
  78. package/src/rust/components/datetime_column_style.rs +0 -3
  79. package/src/rust/components/empty_row.rs +0 -3
  80. package/src/rust/components/expression_editor.rs +2 -3
  81. package/src/rust/components/filter_dropdown.rs +3 -3
  82. package/src/rust/components/font_loader.rs +2 -2
  83. package/src/rust/components/form/code_editor.rs +0 -3
  84. package/src/rust/components/form/debug.rs +3 -4
  85. package/src/rust/components/function_dropdown.rs +3 -3
  86. package/src/rust/components/global_filter_bar.rs +76 -0
  87. package/src/rust/components/main_panel/frame_theme.rs +186 -0
  88. package/src/rust/components/main_panel/msg.rs +62 -0
  89. package/src/rust/components/main_panel/presize.rs +220 -0
  90. package/src/rust/components/main_panel/reconcile.rs +224 -0
  91. package/src/rust/components/main_panel/render.rs +385 -0
  92. package/src/rust/components/main_panel/update.rs +184 -0
  93. package/src/rust/components/main_panel.rs +311 -71
  94. package/src/rust/components/mod.rs +4 -0
  95. package/src/rust/components/number_series_style.rs +0 -3
  96. package/src/rust/components/panel_menu.rs +486 -0
  97. package/src/rust/components/panel_tab.rs +505 -0
  98. package/src/rust/components/plugin_selector.rs +0 -3
  99. package/src/rust/components/plugin_tab.rs +1 -6
  100. package/src/rust/components/portal.rs +5 -2
  101. package/src/rust/components/render_warning.rs +0 -3
  102. package/src/rust/components/settings_panel.rs +13 -5
  103. package/src/rust/components/status_bar.rs +113 -180
  104. package/src/rust/components/status_indicator.rs +6 -9
  105. package/src/rust/components/string_column_style.rs +0 -3
  106. package/src/rust/components/style/mod.rs +65 -27
  107. package/src/rust/components/style/style_provider.rs +51 -16
  108. package/src/rust/components/style_controls/number_string_format.rs +1 -8
  109. package/src/rust/components/type_icon.rs +1 -4
  110. package/src/rust/components/viewer/filters.rs +133 -0
  111. package/src/rust/components/viewer/msg.rs +180 -0
  112. package/src/rust/components/viewer/panels.rs +326 -0
  113. package/src/rust/components/viewer/render.rs +243 -0
  114. package/src/rust/components/viewer/settings.rs +402 -0
  115. package/src/rust/components/viewer/snapshots.rs +134 -0
  116. package/src/rust/components/viewer/wiring.rs +505 -0
  117. package/src/rust/components/viewer.rs +243 -758
  118. package/src/rust/config/export_method.rs +1 -1
  119. package/src/rust/config/mod.rs +4 -0
  120. package/src/rust/config/options.rs +71 -0
  121. package/src/rust/config/viewer_config.rs +24 -2
  122. package/src/rust/config/workspace_config.rs +92 -0
  123. package/src/rust/custom_elements/copy_dropdown.rs +9 -3
  124. package/src/rust/custom_elements/debug_plugin.rs +20 -1
  125. package/src/rust/custom_elements/export_dropdown.rs +9 -3
  126. package/src/rust/custom_elements/viewer.rs +1015 -286
  127. package/src/rust/custom_events.rs +212 -61
  128. package/src/rust/js/mod.rs +2 -0
  129. package/src/rust/js/plugin.rs +297 -10
  130. package/src/rust/js/regular_layout.rs +258 -0
  131. package/src/rust/lib.rs +20 -2
  132. package/src/rust/presentation.rs +66 -33
  133. package/src/rust/queries/get_viewer_config.rs +29 -11
  134. package/src/rust/queries/plugin_column_styles.rs +2 -2
  135. package/src/rust/renderer/activate.rs +41 -14
  136. package/src/rust/renderer/dispatch.rs +436 -0
  137. package/src/rust/renderer/plugin_config.rs +495 -0
  138. package/src/rust/renderer/registry.rs +18 -6
  139. package/src/rust/renderer/render_timer.rs +29 -10
  140. package/src/rust/renderer.rs +350 -827
  141. package/src/rust/session/column_defaults_update.rs +26 -8
  142. package/src/rust/session/metadata.rs +11 -1
  143. package/src/rust/session/view_subscription.rs +30 -15
  144. package/src/rust/session.rs +753 -251
  145. package/src/rust/tasks/apply_global_filters.rs +89 -0
  146. package/src/rust/tasks/auto_pause.rs +171 -0
  147. package/src/rust/tasks/copy_export.rs +29 -19
  148. package/src/rust/tasks/create_panel.rs +243 -0
  149. package/src/rust/tasks/dismiss_render_warning.rs +3 -1
  150. package/src/rust/tasks/edit_expression.rs +4 -4
  151. package/src/rust/tasks/eject.rs +64 -23
  152. package/src/rust/tasks/mod.rs +28 -4
  153. package/src/rust/tasks/pipeline.rs +411 -0
  154. package/src/rust/tasks/presize_panels.rs +324 -0
  155. package/src/rust/tasks/reset_all.rs +22 -12
  156. package/src/rust/tasks/resize_observer.rs +124 -87
  157. package/src/rust/tasks/restore_and_render.rs +80 -86
  158. package/src/rust/tasks/restore_panel.rs +145 -0
  159. package/src/rust/tasks/send_column_config.rs +5 -2
  160. package/src/rust/tasks/send_plugin_config.rs +5 -2
  161. package/src/rust/tasks/set_edit_mode.rs +59 -0
  162. package/src/rust/tasks/sync_update_panels.rs +152 -0
  163. package/src/rust/tasks/table_lifecycle.rs +158 -0
  164. package/src/rust/tasks/update_theme.rs +56 -12
  165. package/src/rust/utils/completion.rs +56 -0
  166. package/src/rust/utils/custom_element.rs +5 -0
  167. package/src/rust/utils/debounce.rs +130 -7
  168. package/src/rust/utils/mod.rs +6 -0
  169. package/src/rust/utils/pending_effects.rs +62 -0
  170. package/src/rust/{components/style/local_style.rs → utils/spawn.rs} +17 -19
  171. package/src/rust/utils/weak_scope.rs +0 -10
  172. package/src/rust/workspace.rs +846 -0
  173. package/src/svg/active-indicator.svg +3 -0
  174. package/src/themes/botanical.css +7 -2
  175. package/src/themes/defaults.css +54 -2
  176. package/src/themes/dracula.css +8 -3
  177. package/src/themes/gruvbox-dark.css +7 -2
  178. package/src/themes/gruvbox.css +5 -2
  179. package/src/themes/icons.css +4 -2
  180. package/src/themes/monokai.css +7 -2
  181. package/src/themes/phosphor.css +7 -2
  182. package/src/themes/pro-dark.css +7 -2
  183. package/src/themes/pro.css +2 -1
  184. package/src/themes/solarized-dark.css +5 -2
  185. package/src/themes/solarized.css +5 -2
  186. package/src/themes/vaporwave.css +7 -2
  187. package/src/ts/bootstrap.ts +8 -7
  188. package/src/ts/extensions.ts +30 -2
  189. package/src/ts/perspective-viewer.ts +10 -0
  190. package/src/ts/plugin.ts +72 -5
  191. package/src/ts/ts-rs/ClientOptions.ts +7 -0
  192. package/src/ts/ts-rs/ExportMethod.ts +3 -0
  193. package/src/ts/ts-rs/ExportOptions.ts +8 -0
  194. package/src/ts/ts-rs/GetClientOptions.ts +7 -0
  195. package/src/ts/ts-rs/GetTableOptions.ts +7 -0
  196. package/src/ts/ts-rs/Layout.ts +9 -0
  197. package/src/ts/ts-rs/Orientation.ts +6 -0
  198. package/src/ts/ts-rs/PanelOptions.ts +7 -0
  199. package/src/ts/ts-rs/PanelViewerConfig.ts +15 -0
  200. package/src/ts/ts-rs/SplitLayout.ts +5 -0
  201. package/src/ts/ts-rs/TabLayout.ts +11 -0
  202. package/src/ts/ts-rs/ViewerConfig.ts +6 -2
  203. package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
  204. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
  205. package/tsconfig.json +1 -1
  206. package/src/rust/components/style/style_cache.rs +0 -136
  207. package/src/rust/tasks/intersection_observer.rs +0 -98
  208. package/src/rust/tasks/update_and_render.rs +0 -110
  209. /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline0.js → perspective-viewer-daeef4ceb066e6c7/inline1.js} +0 -0
  210. /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline1.js → perspective-viewer-daeef4ceb066e6c7/inline2.js} +0 -0
  211. /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline2.js → perspective-viewer-daeef4ceb066e6c7/inline3.js} +0 -0
  212. /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline4.js → perspective-viewer-daeef4ceb066e6c7/inline5.js} +0 -0
@@ -10,9 +10,28 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
+ use wasm_bindgen::{JsCast, JsValue};
13
14
  use yew::prelude::*;
14
15
 
15
- use super::style_cache::StyleCache;
16
+ use super::surface_sheet;
17
+
18
+ /// Which prebuilt CSS bundle a [`StyleProvider`] adopts into its root.
19
+ #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
20
+ pub enum StyleSurface {
21
+ /// The `<perspective-viewer>` ShadowRoot.
22
+ #[default]
23
+ Viewer,
24
+ /// Column-name completion dropdown (inverted color scheme).
25
+ ColumnDropdown,
26
+ /// Filter-value completion dropdown.
27
+ FilterDropdown,
28
+ /// Expression function-completion dropdown.
29
+ FunctionDropdown,
30
+ /// Copy / Export menu (non-inverted color scheme).
31
+ DropdownMenu,
32
+ /// Panel context menu (right-click a panel).
33
+ ContextMenu,
34
+ }
16
35
 
17
36
  #[derive(Properties, PartialEq)]
18
37
  pub struct StyleProviderProps {
@@ -20,32 +39,48 @@ pub struct StyleProviderProps {
20
39
 
21
40
  #[prop_or(true)]
22
41
  pub is_shadow: bool,
42
+
43
+ #[prop_or_default]
44
+ pub surface: StyleSurface,
45
+
23
46
  pub children: Children,
24
47
  }
25
48
 
26
- /// A context which injects any CSS snippet registered within its tree, doing
27
- /// so only once for each unqiue snippet name.
28
- ///
29
- /// CSS can be registered within sub-components via the `<LocalStyle>` component
30
- /// and `css!()` resource inlining macro.
31
- pub struct StyleProvider {
32
- cache: StyleCache,
33
- }
49
+ /// Adopts the build-time CSS bundle for its [`StyleSurface`] into the root's
50
+ /// `adoptedStyleSheets` once, on creation. Unlike the previous per-snippet
51
+ /// `LocalStyle` registration, children no longer register CSS individually.
52
+ pub struct StyleProvider;
34
53
 
35
54
  impl Component for StyleProvider {
36
55
  type Message = ();
37
56
  type Properties = StyleProviderProps;
38
57
 
39
58
  fn create(ctx: &Context<Self>) -> Self {
40
- let cache = StyleCache::new(ctx.props().is_shadow, &ctx.props().root);
41
- Self { cache }
59
+ adopt_surface_sheet(ctx.props());
60
+ Self
42
61
  }
43
62
 
44
63
  fn view(&self, ctx: &Context<Self>) -> Html {
45
- html! {
46
- <ContextProvider<StyleCache> context={self.cache.clone()}>
47
- { for ctx.props().children.iter() }
48
- </ContextProvider<StyleCache>>
49
- }
64
+ html! { <>{ for ctx.props().children.iter() }</> }
65
+ }
66
+ }
67
+
68
+ /// Push the surface's shared stylesheet onto the root's `adoptedStyleSheets`,
69
+ /// unless it is already present (a root may host more than one provider).
70
+ fn adopt_surface_sheet(props: &StyleProviderProps) {
71
+ let root: JsValue = if props.is_shadow {
72
+ props.root.shadow_root().unwrap().into()
73
+ } else {
74
+ web_sys::window().unwrap().document().unwrap().into()
75
+ };
76
+
77
+ let sheets = js_sys::Reflect::get(&root, &"adoptedStyleSheets".into())
78
+ .unwrap()
79
+ .unchecked_into::<js_sys::Array>();
80
+
81
+ let sheet = surface_sheet(props.surface);
82
+ let sheet_val: &JsValue = sheet.as_ref();
83
+ if sheets.index_of(sheet_val, 0) < 0 {
84
+ sheets.push(sheet_val);
50
85
  }
51
86
  }
@@ -19,9 +19,7 @@ use perspective_client::config::ColumnType;
19
19
  pub use types::*;
20
20
  use yew::{Callback, Component, Properties, html};
21
21
 
22
- use crate::components::style::LocalStyle;
23
22
  use crate::config::*;
24
- use crate::css;
25
23
 
26
24
  #[derive(Properties, PartialEq, Clone)]
27
25
  pub struct CustomNumberFormatProps {
@@ -211,12 +209,7 @@ impl Component for CustomNumberFormat {
211
209
 
212
210
  fn view(&self, ctx: &yew::prelude::Context<Self>) -> yew::prelude::Html {
213
211
  html! {
214
- <>
215
- <LocalStyle href={css!("column-style")} />
216
- { self.style_section(ctx) }
217
- { self.digits_section(ctx) }
218
- { self.misc_section(ctx) }
219
- </>
212
+ <>{ self.style_section(ctx) }{ self.digits_section(ctx) }{ self.misc_section(ctx) }</>
220
213
  }
221
214
  }
222
215
  }
@@ -14,9 +14,6 @@ use perspective_client::config::ColumnType;
14
14
  use yew::html::IntoPropValue;
15
15
  use yew::{Properties, classes, function_component, html};
16
16
 
17
- use crate::components::style::LocalStyle;
18
- use crate::css;
19
-
20
17
  #[derive(PartialEq, Debug, Clone, Copy)]
21
18
  pub enum TypeIconType {
22
19
  Type(ColumnType),
@@ -31,7 +28,7 @@ pub struct TypeIconProps {
31
28
  #[function_component(TypeIcon)]
32
29
  pub fn type_icon(p: &TypeIconProps) -> yew::Html {
33
30
  let class = classes!(p.ty.to_string(), "type-icon");
34
- html! { <><LocalStyle href={css!("type-icon")} /><span {class} /></> }
31
+ html! { <><span {class} /></> }
35
32
  }
36
33
 
37
34
  impl From<ColumnType> for TypeIconType {
@@ -0,0 +1,133 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ //! Master/detail cross-filtering handlers. The filter set itself is OWNED BY
14
+ //! THE [`Workspace`] model (fed by master-panel selections via the
15
+ //! `perspective-global-filter` listener in [`super::wiring`], surfaced as
16
+ //! chips in the `StatusBar`); these handlers mutate it and run the
17
+ //! [`apply_global_filters`] task. Re-renders flow through the workspace's
18
+ //! `filters_changed` PubSub → `UpdateGlobalFilters` snapshot refresh.
19
+ //!
20
+ //! [`Workspace`]: crate::workspace::Workspace
21
+
22
+ use yew::prelude::*;
23
+
24
+ use super::PerspectiveViewer;
25
+ use super::msg::MasterSelection;
26
+ use crate::tasks::*;
27
+ use crate::workspace::PanelId;
28
+
29
+ impl PerspectiveViewer {
30
+ pub(super) fn on_toggle_master(&mut self, ctx: &Context<Self>, id: String) -> bool {
31
+ let id = PanelId::from(id);
32
+ let is_master = ctx.props().workspace.toggle_master(&id);
33
+ // Drive the panel's plugin into (out of) a selection mode so a
34
+ // master emits `perspective-global-filter` selections via the
35
+ // plugin's built-in select mechanism. No-op for plugins without
36
+ // an `edit_mode` (e.g. charts, which select intrinsically).
37
+ if let Some(panel) = ctx.props().workspace.panel(&id) {
38
+ let mode = if is_master {
39
+ "SELECT_ROW_TREE"
40
+ } else {
41
+ "READ_ONLY"
42
+ };
43
+ set_edit_mode(&panel.session, &panel.renderer, mode);
44
+ }
45
+ // Demoting a master back to detail retracts (only) its own
46
+ // contribution — other masters' clauses and any restored bucket
47
+ // survive. Promotion needs no filter mutation: the re-stamp below
48
+ // grants the new master immunity (and details keep filtering).
49
+ if !is_master {
50
+ ctx.props().workspace.clear_contribution(&id);
51
+ }
52
+ apply_global_filters(&ctx.props().workspace);
53
+ // Re-render regardless of a filter change — the context menu's
54
+ // "Master"/"Detail" label reads the toggled state.
55
+ true
56
+ }
57
+
58
+ pub(super) fn on_master_contribution(
59
+ &mut self,
60
+ ctx: &Context<Self>,
61
+ panel_id: String,
62
+ selection: Option<MasterSelection>,
63
+ ) -> bool {
64
+ // A master's selection state REPLACES its per-panel contribution to
65
+ // the element-level global filter set (`None` = deselected = clears
66
+ // it) — then surfaces in the bar and applies to the details. The
67
+ // re-render arrives via `filters_changed` → `UpdateGlobalFilters`.
68
+ // Contributions are keyed by the ORIGINATING panel, so removing them
69
+ // from the bar can clear its selection state. Non-master events
70
+ // (plain detail-panel selections/clicks) don't broadcast.
71
+ let workspace = &ctx.props().workspace;
72
+ let id = PanelId::from(panel_id);
73
+ if !workspace.is_master(&id) {
74
+ return false;
75
+ }
76
+
77
+ let filters = match selection {
78
+ None => Vec::new(),
79
+ Some(selection) => {
80
+ // Broadcast only the selection-DERIVED clauses: the event's
81
+ // filters embed the master's own stored `filter` field (a
82
+ // select detail's configs extend the panel config), which
83
+ // must not leak into the details — subtract it. (Live `Ref`
84
+ // guard — scope it to this statement.)
85
+ let own = workspace
86
+ .panel(&id)
87
+ .map(|p| p.session.get_view_config().filter.clone())
88
+ .unwrap_or_default();
89
+
90
+ let derived = selection
91
+ .filters
92
+ .into_iter()
93
+ .filter(|x| !own.contains(x))
94
+ .collect::<Vec<_>>();
95
+
96
+ if !derived.is_empty() {
97
+ derived
98
+ } else {
99
+ // Nothing derivable from the event's configs — fall back
100
+ // to the clicked-cell clause if the detail carried one,
101
+ // else leave the current contribution untouched.
102
+ match selection.cell_fallback {
103
+ Some(x) => vec![x],
104
+ None => return false,
105
+ }
106
+ }
107
+ },
108
+ };
109
+
110
+ workspace.set_contribution(&id, filters);
111
+ apply_global_filters(workspace);
112
+ false
113
+ }
114
+
115
+ pub(super) fn on_remove_global_filter(&mut self, ctx: &Context<Self>, index: usize) -> bool {
116
+ let workspace = &ctx.props().workspace;
117
+ let origins = workspace.remove_global_filter(index);
118
+ apply_global_filters(workspace);
119
+ // The chip's clause is gone — the master selection(s) that produced
120
+ // it must not stay highlighted, implying a filter that no longer
121
+ // exists. (Restored/unattributed clauses have no origin — no-op.)
122
+ clear_master_selections(workspace, origins);
123
+ false
124
+ }
125
+
126
+ pub(super) fn on_clear_global_filters(&mut self, ctx: &Context<Self>) -> bool {
127
+ let workspace = &ctx.props().workspace;
128
+ let origins = workspace.clear_global_filters();
129
+ apply_global_filters(workspace);
130
+ clear_master_selections(workspace, origins);
131
+ false
132
+ }
133
+ }
@@ -0,0 +1,180 @@
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
+ //! The root component's message protocol. One flat enum — the `update()` match
14
+ //! in `viewer.rs` is its dispatch table; handler bodies live in the sibling
15
+ //! domain modules ([`super::panels`], [`super::settings`], [`super::filters`],
16
+ //! [`super::snapshots`]).
17
+
18
+ use futures::channel::oneshot::Sender;
19
+ use perspective_client::config::Filter;
20
+ use perspective_js::utils::ApiResult;
21
+ use wasm_bindgen::JsValue;
22
+
23
+ use crate::components::settings_panel::SelectedTab;
24
+ use crate::config::*;
25
+ use crate::presentation::{ColumnLocator, ColumnSettingsTab, DragDropProps, PresentationProps};
26
+ use crate::renderer::RendererProps;
27
+ use crate::session::{SessionProps, TableLoadState, ViewStats};
28
+ use crate::utils::Completion;
29
+
30
+ /// The filter-bearing payload of a master panel's selection or click event
31
+ /// (`MasterContribution`), as decoded by the host listeners in
32
+ /// [`super::wiring`].
33
+ #[derive(Debug)]
34
+ pub struct MasterSelection {
35
+ /// The event's filter clauses (`insertFilters` for the select-detail
36
+ /// family, `config.filter` for clicks) — BEFORE the master's own stored
37
+ /// filters are subtracted (see `on_master_contribution`).
38
+ pub filters: Vec<Filter>,
39
+
40
+ /// A synthesized clicked-cell `[column, "==", value]` clause, used when
41
+ /// `filters` derives to nothing — e.g. a FLAT (un-grouped) datagrid
42
+ /// master, whose clicks carry no group-by path to filter on.
43
+ pub cell_fallback: Option<Filter>,
44
+ }
45
+
46
+ #[derive(Debug)]
47
+ pub enum PerspectiveViewerMsg {
48
+ ColumnSettingsPanelSizeUpdate(Option<i32>),
49
+ ColumnSettingsTabChanged(ColumnSettingsTab),
50
+ OpenColumnSettings {
51
+ locator: Option<ColumnLocator>,
52
+ sender: Option<Sender<()>>,
53
+ toggle: bool,
54
+ },
55
+ PreloadFontsUpdate,
56
+
57
+ /// Element-level reset (the public `reset()` API): reset EVERY panel and
58
+ /// clear the cross-filter overlay, symmetric with whole-element
59
+ /// `save`/`restore`. The `bool` also clears expressions/column settings.
60
+ Reset(bool, Option<Completion>),
61
+
62
+ /// Reset ONLY the named panel — or the active panel when `None` — to its
63
+ /// default `ViewerConfig` (the toolbar Reset button, the context menu's
64
+ /// "Reset" command, and the public `resetPanel()` API). The `bool` is
65
+ /// `Reset`'s expressions flag (toolbar shift-click); the `Completion`
66
+ /// resolves the `resetPanel()` promise after the reset's run completes
67
+ /// (invariant I6).
68
+ ResetPanel(Option<String>, bool, Option<Completion>),
69
+ Resize,
70
+
71
+ /// The set of layout panels changed (added/removed); re-render so the
72
+ /// layout host reconciles its `<regular-layout>` cells.
73
+ LayoutChanged,
74
+
75
+ /// Make the named panel active: re-target the settings panel + status bar
76
+ /// (and the root's session/renderer subscriptions) to its engines. The
77
+ /// `Completion` resolves `setActivePanel()` after the activation-chrome
78
+ /// nudge runs complete (invariant I6).
79
+ SetActivePanel(String, Option<Completion>),
80
+
81
+ /// The named panel's frame was closed (removed from the layout); remove it
82
+ /// from the workspace and dispose its engines. The `Completion` resolves
83
+ /// `removePanel()` after the eject's teardown run completes (invariant
84
+ /// I6) — carrying any teardown error, which was previously dropped.
85
+ ClosePanel(String, Option<Completion>),
86
+
87
+ /// Whole-element `restore` finished replacing the panel set in the
88
+ /// `Workspace` (new models inserted, old panels ejected, layout staged):
89
+ /// activate the named panel, re-subscribe the per-panel wiring, and
90
+ /// re-render — the SINGLE visible commit of the whole restore.
91
+ CommitWorkspaceRestore(String),
92
+
93
+ /// Duplicate the named panel: snapshot its config into a new independent
94
+ /// panel appended to the layout.
95
+ DuplicatePanel(String),
96
+
97
+ /// New panel: a fresh (default-config) panel bound to the named panel's
98
+ /// table (from the default client).
99
+ NewPanel(String),
100
+
101
+ /// New panel bound to the named `Table` on the named `Client` (the
102
+ /// context menu's "New" sub-menu). The `Client` is resolved by name from
103
+ /// the `Workspace` loaded-clients registry.
104
+ NewPanelFrom {
105
+ client: String,
106
+ table: String,
107
+ },
108
+
109
+ /// Toggle the named panel's master/detail (filter-source) role.
110
+ ToggleMaster(String),
111
+
112
+ /// A master panel's selection state, from EITHER host listener
113
+ /// (`perspective-global-filter` select/deselect or `perspective-click`):
114
+ /// `Some` REPLACES that panel's global-filter contribution, `None`
115
+ /// (deselect) clears it. Non-master sources are ignored by the handler.
116
+ MasterContribution(String, Option<MasterSelection>),
117
+
118
+ /// Remove the global filter at this index (GlobalFilterBar chip ×).
119
+ RemoveGlobalFilter(usize),
120
+
121
+ /// Clear all global filters (GlobalFilterBar "Clear").
122
+ ClearGlobalFilters,
123
+
124
+ /// Some panel's title changed (any panel, via `_title_subscriptions`);
125
+ /// re-render so the tab titles refresh.
126
+ TitlesChanged,
127
+ SettingsPanelSizeUpdate(Option<i32>),
128
+
129
+ /// The settings-pane divider proposed a new pane width (per pointermove,
130
+ /// from the *deferred* `SplitPanel` — it has NOT been applied). Feeds the
131
+ /// latest-wins presize pump (`PRESIZE_EVERYWHERE_PLAN.md` P1): geometry
132
+ /// commits only after every visible panel has rendered at its target.
133
+ SettingsDividerMove(i32),
134
+
135
+ /// Run one pump iteration: presize all visible panels at the newest
136
+ /// proposed pane width, then commit it.
137
+ SettingsDividerPump,
138
+
139
+ /// Presize for this pane width completed — commit it (the deferred
140
+ /// `SplitPanel`'s controlled `size`), then pump again if a newer target
141
+ /// arrived meanwhile.
142
+ SettingsDividerCommit(i32),
143
+
144
+ /// Divider drag ended: reactively finalize every visible panel at its
145
+ /// exact settled cell (debounced no-op when the presizes were exact).
146
+ SettingsDividerFinish,
147
+ SettingsPanelTabChanged(SelectedTab),
148
+ SettingsPanelAutoWidth(f64),
149
+ ToggleDebug,
150
+ ToggleSettingsComplete(SettingsUpdate, Sender<()>),
151
+ ToggleSettingsInit(Option<SettingsUpdate>, Option<Sender<ApiResult<JsValue>>>),
152
+ UpdateSession(Box<SessionProps>),
153
+ UpdateRenderer(Box<RendererProps>),
154
+ UpdatePresentation(Box<PresentationProps>),
155
+
156
+ /// Update only `is_settings_open` in the presentation snapshot without
157
+ /// touching `available_themes` (which requires async data).
158
+ UpdateSettingsOpen(bool),
159
+ UpdateIsWorkspace(bool),
160
+
161
+ /// Update only `open_column_settings` in the presentation snapshot.
162
+ UpdateColumnSettings(Box<crate::presentation::OpenColumnSettings>),
163
+ UpdateDragDrop(Box<DragDropProps>),
164
+
165
+ /// Update only stats-related fields of `session_props` without touching
166
+ /// `config`. This prevents `stats_changed` events (e.g. from `reset()`)
167
+ /// from propagating a freshly-cleared config to the column selector.
168
+ UpdateSessionStats(Option<ViewStats>, Option<TableLoadState>),
169
+
170
+ /// Refresh the root's render snapshot of the `Workspace`-owned global
171
+ /// filter set (dispatched by its `filters_changed` PubSub).
172
+ UpdateGlobalFilters,
173
+
174
+ /// The active panel's in-flight config-run count changed. LEVEL-
175
+ /// triggered: the payload is the ABSOLUTE count (RAII-settled — see
176
+ /// `Session::begin_config_run`), which the handler ASSIGNS to
177
+ /// `update_count`; there is no delta arithmetic to drift. Threaded to
178
+ /// `StatusIndicator` as the "updating" spinner.
179
+ UpdateInFlight(u32),
180
+ }