@perspective-dev/viewer 4.5.2 → 5.1.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.
- package/dist/cdn/perspective-viewer.js +20 -1
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/phosphor.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/esm/extensions.d.ts +22 -3
- package/dist/esm/perspective-viewer.d.ts +9 -0
- package/dist/esm/perspective-viewer.inline.js +20 -1
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +20 -1
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/plugin.d.ts +71 -5
- package/dist/esm/ts-rs/ClientOptions.d.ts +7 -0
- package/dist/esm/ts-rs/ExportMethod.d.ts +1 -0
- package/dist/esm/ts-rs/ExportOptions.d.ts +9 -0
- package/dist/esm/ts-rs/GetClientOptions.d.ts +8 -0
- package/dist/esm/ts-rs/GetTableOptions.d.ts +8 -0
- package/dist/esm/ts-rs/Layout.d.ts +11 -0
- package/dist/esm/ts-rs/Orientation.d.ts +4 -0
- package/dist/esm/ts-rs/PanelOptions.d.ts +7 -0
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +41 -0
- package/dist/esm/ts-rs/SplitLayout.d.ts +7 -0
- package/dist/esm/ts-rs/TabLayout.d.ts +10 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +8 -2
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +9 -0
- package/dist/esm/ts-rs/WindowAggregate.d.ts +1 -0
- package/dist/esm/ts-rs/WindowSort.d.ts +7 -0
- package/dist/esm/ts-rs/WindowSortDir.d.ts +6 -0
- package/dist/esm/ts-rs/WindowSpec.d.ts +46 -0
- package/dist/esm/ts-rs/Windows.d.ts +10 -0
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +40 -0
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +28 -0
- package/dist/wasm/perspective-viewer.d.ts +299 -78
- package/dist/wasm/perspective-viewer.js +544 -186
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +34 -26
- package/dist/wasm/snippets/perspective-viewer-8a8c7d5d696b43ae/inline0.js +6 -0
- package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline3.js → perspective-viewer-8a8c7d5d696b43ae/inline4.js} +5 -0
- package/package.json +3 -2
- package/src/css/_context_menu.css +14 -0
- package/src/css/column-selector.css +1 -0
- package/src/css/column-settings-panel.css +68 -3
- package/src/css/column-style.css +4 -3
- package/src/css/config-selector.css +22 -1
- package/src/css/containers/context-menu.css +107 -0
- package/src/css/containers/tabs.css +2 -2
- package/src/css/panel-tab.css +193 -0
- package/src/css/plugin-selector.css +7 -10
- package/src/css/status-bar.css +75 -81
- package/src/css/viewer.css +188 -70
- package/src/rust/components/column_dropdown.rs +40 -30
- package/src/rust/components/column_selector/active_column.rs +28 -45
- package/src/rust/components/column_selector/aggregate_selector.rs +4 -7
- package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
- package/src/rust/components/column_selector/config_selector.rs +40 -82
- package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
- package/src/rust/components/column_selector/filter_column.rs +5 -15
- package/src/rust/components/column_selector/inactive_column.rs +11 -9
- package/src/rust/components/column_selector/sort_column.rs +3 -6
- package/src/rust/components/column_selector.rs +25 -14
- package/src/rust/components/column_settings_sidebar/style_tab.rs +2 -1
- package/src/rust/components/column_settings_sidebar/window_tab.rs +34 -0
- package/src/rust/components/column_settings_sidebar.rs +163 -16
- package/src/rust/components/containers/dragdrop_list.rs +73 -3
- package/src/rust/components/containers/sidebar.rs +99 -22
- package/src/rust/components/containers/split_panel.rs +181 -62
- package/src/rust/components/context_menu.rs +130 -0
- package/src/rust/components/editable_header.rs +2 -0
- package/src/rust/components/form/debug.rs +3 -0
- package/src/rust/components/global_filter_bar.rs +76 -0
- package/src/rust/components/main_panel/frame_theme.rs +186 -0
- package/src/rust/components/main_panel/msg.rs +62 -0
- package/src/rust/components/main_panel/presize.rs +220 -0
- package/src/rust/components/main_panel/reconcile.rs +224 -0
- package/src/rust/components/main_panel/render.rs +385 -0
- package/src/rust/components/main_panel/update.rs +184 -0
- package/src/rust/components/main_panel.rs +316 -71
- package/src/rust/components/mod.rs +5 -0
- package/src/rust/components/panel_menu.rs +493 -0
- package/src/rust/components/panel_tab.rs +510 -0
- package/src/rust/components/plugin_tab.rs +1 -1
- package/src/rust/components/portal.rs +7 -0
- package/src/rust/components/settings_panel.rs +13 -5
- package/src/rust/components/status_bar.rs +110 -178
- package/src/rust/components/status_indicator.rs +6 -9
- package/src/rust/components/style/mod.rs +5 -0
- package/src/rust/components/style/style_provider.rs +2 -0
- package/src/rust/components/viewer/filters.rs +133 -0
- package/src/rust/components/viewer/msg.rs +182 -0
- package/src/rust/components/viewer/panels.rs +326 -0
- package/src/rust/components/viewer/render.rs +255 -0
- package/src/rust/components/viewer/settings.rs +434 -0
- package/src/rust/components/viewer/snapshots.rs +134 -0
- package/src/rust/components/viewer/wiring.rs +505 -0
- package/src/rust/components/viewer.rs +245 -756
- package/src/rust/components/window_editor.rs +1013 -0
- package/src/rust/config/export_method.rs +1 -1
- package/src/rust/config/mod.rs +4 -0
- package/src/rust/config/options.rs +71 -0
- package/src/rust/config/viewer_config.rs +24 -2
- package/src/rust/config/workspace_config.rs +92 -0
- package/src/rust/custom_elements/copy_dropdown.rs +3 -1
- package/src/rust/custom_elements/debug_plugin.rs +20 -1
- package/src/rust/custom_elements/export_dropdown.rs +3 -1
- package/src/rust/custom_elements/viewer.rs +1015 -286
- package/src/rust/custom_events.rs +212 -61
- package/src/rust/js/mod.rs +2 -0
- package/src/rust/js/plugin.rs +297 -10
- package/src/rust/js/regular_layout.rs +258 -0
- package/src/rust/lib.rs +24 -2
- package/src/rust/presentation/column_locator.rs +16 -1
- package/src/rust/presentation.rs +66 -33
- package/src/rust/queries/columns_iter_set.rs +7 -1
- package/src/rust/queries/get_viewer_config.rs +29 -11
- package/src/rust/queries/plugin_column_styles.rs +2 -2
- package/src/rust/renderer/activate.rs +41 -14
- package/src/rust/renderer/dispatch.rs +463 -0
- package/src/rust/renderer/plugin_config.rs +495 -0
- package/src/rust/renderer/registry.rs +18 -6
- package/src/rust/renderer/render_timer.rs +29 -10
- package/src/rust/renderer.rs +384 -838
- package/src/rust/session/column_defaults_update.rs +26 -8
- package/src/rust/session/drag_drop_update.rs +18 -0
- package/src/rust/session/metadata.rs +66 -9
- package/src/rust/session/replace_expression_update.rs +1 -0
- package/src/rust/session/view_subscription.rs +30 -15
- package/src/rust/session.rs +755 -264
- package/src/rust/tasks/apply_global_filters.rs +89 -0
- package/src/rust/tasks/auto_pause.rs +171 -0
- package/src/rust/tasks/copy_export.rs +29 -19
- package/src/rust/tasks/create_panel.rs +243 -0
- package/src/rust/tasks/dismiss_render_warning.rs +3 -1
- package/src/rust/tasks/edit_expression.rs +4 -4
- package/src/rust/tasks/edit_window.rs +155 -0
- package/src/rust/tasks/eject.rs +64 -23
- package/src/rust/tasks/mod.rs +30 -4
- package/src/rust/tasks/pipeline.rs +411 -0
- package/src/rust/tasks/presize_panels.rs +324 -0
- package/src/rust/tasks/reset_all.rs +22 -12
- package/src/rust/tasks/resize_observer.rs +124 -87
- package/src/rust/tasks/restore_and_render.rs +80 -86
- package/src/rust/tasks/restore_panel.rs +145 -0
- package/src/rust/tasks/send_column_config.rs +5 -2
- package/src/rust/tasks/send_plugin_config.rs +5 -2
- package/src/rust/tasks/set_edit_mode.rs +59 -0
- package/src/rust/tasks/sync_update_panels.rs +152 -0
- package/src/rust/tasks/table_lifecycle.rs +158 -0
- package/src/rust/tasks/update_theme.rs +56 -12
- package/src/rust/utils/browser/dragdrop.rs +12 -0
- package/src/rust/utils/browser/tests/debounce.rs +49 -0
- package/src/rust/utils/completion.rs +56 -0
- package/src/rust/utils/custom_element.rs +5 -0
- package/src/rust/utils/debounce.rs +189 -17
- package/src/rust/utils/mod.rs +6 -0
- package/src/rust/utils/pending_effects.rs +62 -0
- package/src/rust/utils/spawn.rs +33 -0
- package/src/rust/utils/weak_scope.rs +0 -10
- package/src/rust/workspace.rs +846 -0
- package/src/svg/active-indicator.svg +3 -0
- package/src/svg/pin-icon.svg +5 -0
- package/src/themes/botanical.css +5 -2
- package/src/themes/defaults.css +54 -2
- package/src/themes/dracula.css +6 -3
- package/src/themes/gruvbox-dark.css +5 -2
- package/src/themes/gruvbox.css +5 -2
- package/src/themes/icons.css +5 -2
- package/src/themes/intl/de.css +7 -0
- package/src/themes/intl/es.css +7 -0
- package/src/themes/intl/fr.css +7 -0
- package/src/themes/intl/ja.css +7 -0
- package/src/themes/intl/pt.css +7 -0
- package/src/themes/intl/zh.css +7 -0
- package/src/themes/intl.css +10 -1
- package/src/themes/monokai.css +5 -2
- package/src/themes/phosphor.css +5 -2
- package/src/themes/pro-dark.css +5 -2
- package/src/themes/pro.css +2 -1
- package/src/themes/solarized-dark.css +5 -2
- package/src/themes/solarized.css +5 -2
- package/src/themes/vaporwave.css +5 -2
- package/src/ts/bootstrap.ts +8 -7
- package/src/ts/extensions.ts +30 -2
- package/src/ts/perspective-viewer.ts +10 -0
- package/src/ts/plugin.ts +72 -5
- package/src/ts/ts-rs/ClientOptions.ts +7 -0
- package/src/ts/ts-rs/ExportMethod.ts +3 -0
- package/src/ts/ts-rs/ExportOptions.ts +8 -0
- package/src/ts/ts-rs/GetClientOptions.ts +7 -0
- package/src/ts/ts-rs/GetTableOptions.ts +7 -0
- package/src/ts/ts-rs/Layout.ts +9 -0
- package/src/ts/ts-rs/Orientation.ts +6 -0
- package/src/ts/ts-rs/PanelOptions.ts +7 -0
- package/src/ts/ts-rs/PanelViewerConfig.ts +16 -0
- package/src/ts/ts-rs/SplitLayout.ts +5 -0
- package/src/ts/ts-rs/TabLayout.ts +11 -0
- package/src/ts/ts-rs/ViewerConfig.ts +7 -2
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +9 -0
- package/src/ts/ts-rs/WindowAggregate.ts +3 -0
- package/src/ts/ts-rs/WindowSort.ts +9 -0
- package/src/ts/ts-rs/WindowSortDir.ts +8 -0
- package/src/ts/ts-rs/WindowSpec.ts +46 -0
- package/src/ts/ts-rs/Windows.ts +10 -0
- package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
- package/src/rust/tasks/intersection_observer.rs +0 -98
- package/src/rust/tasks/update_and_render.rs +0 -110
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline0.js → perspective-viewer-8a8c7d5d696b43ae/inline1.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline1.js → perspective-viewer-8a8c7d5d696b43ae/inline2.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline2.js → perspective-viewer-8a8c7d5d696b43ae/inline3.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline4.js → perspective-viewer-8a8c7d5d696b43ae/inline5.js} +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
ColumnSettingsPanelAutoWidth(f64),
|
|
50
|
+
ToggleColumnSettingsPin,
|
|
51
|
+
ColumnSettingsTabChanged(ColumnSettingsTab),
|
|
52
|
+
OpenColumnSettings {
|
|
53
|
+
locator: Option<ColumnLocator>,
|
|
54
|
+
sender: Option<Sender<()>>,
|
|
55
|
+
toggle: bool,
|
|
56
|
+
},
|
|
57
|
+
PreloadFontsUpdate,
|
|
58
|
+
|
|
59
|
+
/// Element-level reset (the public `reset()` API): reset EVERY panel and
|
|
60
|
+
/// clear the cross-filter overlay, symmetric with whole-element
|
|
61
|
+
/// `save`/`restore`. The `bool` also clears expressions/column settings.
|
|
62
|
+
Reset(bool, Option<Completion>),
|
|
63
|
+
|
|
64
|
+
/// Reset ONLY the named panel — or the active panel when `None` — to its
|
|
65
|
+
/// default `ViewerConfig` (the toolbar Reset button, the context menu's
|
|
66
|
+
/// "Reset" command, and the public `resetPanel()` API). The `bool` is
|
|
67
|
+
/// `Reset`'s expressions flag (toolbar shift-click); the `Completion`
|
|
68
|
+
/// resolves the `resetPanel()` promise after the reset's run completes
|
|
69
|
+
/// (invariant I6).
|
|
70
|
+
ResetPanel(Option<String>, bool, Option<Completion>),
|
|
71
|
+
Resize,
|
|
72
|
+
|
|
73
|
+
/// The set of layout panels changed (added/removed); re-render so the
|
|
74
|
+
/// layout host reconciles its `<regular-layout>` cells.
|
|
75
|
+
LayoutChanged,
|
|
76
|
+
|
|
77
|
+
/// Make the named panel active: re-target the settings panel + status bar
|
|
78
|
+
/// (and the root's session/renderer subscriptions) to its engines. The
|
|
79
|
+
/// `Completion` resolves `setActivePanel()` after the activation-chrome
|
|
80
|
+
/// nudge runs complete (invariant I6).
|
|
81
|
+
SetActivePanel(String, Option<Completion>),
|
|
82
|
+
|
|
83
|
+
/// The named panel's frame was closed (removed from the layout); remove it
|
|
84
|
+
/// from the workspace and dispose its engines. The `Completion` resolves
|
|
85
|
+
/// `removePanel()` after the eject's teardown run completes (invariant
|
|
86
|
+
/// I6) — carrying any teardown error, which was previously dropped.
|
|
87
|
+
ClosePanel(String, Option<Completion>),
|
|
88
|
+
|
|
89
|
+
/// Whole-element `restore` finished replacing the panel set in the
|
|
90
|
+
/// `Workspace` (new models inserted, old panels ejected, layout staged):
|
|
91
|
+
/// activate the named panel, re-subscribe the per-panel wiring, and
|
|
92
|
+
/// re-render — the SINGLE visible commit of the whole restore.
|
|
93
|
+
CommitWorkspaceRestore(String),
|
|
94
|
+
|
|
95
|
+
/// Duplicate the named panel: snapshot its config into a new independent
|
|
96
|
+
/// panel appended to the layout.
|
|
97
|
+
DuplicatePanel(String),
|
|
98
|
+
|
|
99
|
+
/// New panel: a fresh (default-config) panel bound to the named panel's
|
|
100
|
+
/// table (from the default client).
|
|
101
|
+
NewPanel(String),
|
|
102
|
+
|
|
103
|
+
/// New panel bound to the named `Table` on the named `Client` (the
|
|
104
|
+
/// context menu's "New" sub-menu). The `Client` is resolved by name from
|
|
105
|
+
/// the `Workspace` loaded-clients registry.
|
|
106
|
+
NewPanelFrom {
|
|
107
|
+
client: String,
|
|
108
|
+
table: String,
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/// Toggle the named panel's master/detail (filter-source) role.
|
|
112
|
+
ToggleMaster(String),
|
|
113
|
+
|
|
114
|
+
/// A master panel's selection state, from EITHER host listener
|
|
115
|
+
/// (`perspective-global-filter` select/deselect or `perspective-click`):
|
|
116
|
+
/// `Some` REPLACES that panel's global-filter contribution, `None`
|
|
117
|
+
/// (deselect) clears it. Non-master sources are ignored by the handler.
|
|
118
|
+
MasterContribution(String, Option<MasterSelection>),
|
|
119
|
+
|
|
120
|
+
/// Remove the global filter at this index (GlobalFilterBar chip ×).
|
|
121
|
+
RemoveGlobalFilter(usize),
|
|
122
|
+
|
|
123
|
+
/// Clear all global filters (GlobalFilterBar "Clear").
|
|
124
|
+
ClearGlobalFilters,
|
|
125
|
+
|
|
126
|
+
/// Some panel's title changed (any panel, via `_title_subscriptions`);
|
|
127
|
+
/// re-render so the tab titles refresh.
|
|
128
|
+
TitlesChanged,
|
|
129
|
+
SettingsPanelSizeUpdate(Option<i32>),
|
|
130
|
+
|
|
131
|
+
/// The settings-pane divider proposed a new pane width (per pointermove,
|
|
132
|
+
/// from the *deferred* `SplitPanel` — it has NOT been applied). Feeds the
|
|
133
|
+
/// latest-wins presize pump (`PRESIZE_EVERYWHERE_PLAN.md` P1): geometry
|
|
134
|
+
/// commits only after every visible panel has rendered at its target.
|
|
135
|
+
SettingsDividerMove(i32),
|
|
136
|
+
|
|
137
|
+
/// Run one pump iteration: presize all visible panels at the newest
|
|
138
|
+
/// proposed pane width, then commit it.
|
|
139
|
+
SettingsDividerPump,
|
|
140
|
+
|
|
141
|
+
/// Presize for this pane width completed — commit it (the deferred
|
|
142
|
+
/// `SplitPanel`'s controlled `size`), then pump again if a newer target
|
|
143
|
+
/// arrived meanwhile.
|
|
144
|
+
SettingsDividerCommit(i32),
|
|
145
|
+
|
|
146
|
+
/// Divider drag ended: reactively finalize every visible panel at its
|
|
147
|
+
/// exact settled cell (debounced no-op when the presizes were exact).
|
|
148
|
+
SettingsDividerFinish,
|
|
149
|
+
SettingsPanelTabChanged(SelectedTab),
|
|
150
|
+
SettingsPanelAutoWidth(f64),
|
|
151
|
+
ToggleDebug,
|
|
152
|
+
ToggleSettingsComplete(SettingsUpdate, Sender<()>),
|
|
153
|
+
ToggleSettingsInit(Option<SettingsUpdate>, Option<Sender<ApiResult<JsValue>>>),
|
|
154
|
+
UpdateSession(Box<SessionProps>),
|
|
155
|
+
UpdateRenderer(Box<RendererProps>),
|
|
156
|
+
UpdatePresentation(Box<PresentationProps>),
|
|
157
|
+
|
|
158
|
+
/// Update only `is_settings_open` in the presentation snapshot without
|
|
159
|
+
/// touching `available_themes` (which requires async data).
|
|
160
|
+
UpdateSettingsOpen(bool),
|
|
161
|
+
UpdateIsWorkspace(bool),
|
|
162
|
+
|
|
163
|
+
/// Update only `open_column_settings` in the presentation snapshot.
|
|
164
|
+
UpdateColumnSettings(Box<crate::presentation::OpenColumnSettings>),
|
|
165
|
+
UpdateDragDrop(Box<DragDropProps>),
|
|
166
|
+
|
|
167
|
+
/// Update only stats-related fields of `session_props` without touching
|
|
168
|
+
/// `config`. This prevents `stats_changed` events (e.g. from `reset()`)
|
|
169
|
+
/// from propagating a freshly-cleared config to the column selector.
|
|
170
|
+
UpdateSessionStats(Option<ViewStats>, Option<TableLoadState>),
|
|
171
|
+
|
|
172
|
+
/// Refresh the root's render snapshot of the `Workspace`-owned global
|
|
173
|
+
/// filter set (dispatched by its `filters_changed` PubSub).
|
|
174
|
+
UpdateGlobalFilters,
|
|
175
|
+
|
|
176
|
+
/// The active panel's in-flight config-run count changed. LEVEL-
|
|
177
|
+
/// triggered: the payload is the ABSOLUTE count (RAII-settled — see
|
|
178
|
+
/// `Session::begin_config_run`), which the handler ASSIGNS to
|
|
179
|
+
/// `update_count`; there is no delta arithmetic to drift. Threaded to
|
|
180
|
+
/// `StatusIndicator` as the "updating" spinner.
|
|
181
|
+
UpdateInFlight(u32),
|
|
182
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
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
|
+
//! Panel-lifecycle handlers: the [`Workspace`] panel set (add / duplicate /
|
|
14
|
+
//! close) and active-panel targeting. INVARIANT: app-initiated layout changes
|
|
15
|
+
//! mutate the `Workspace` FIRST, synchronously — `regular-layout` is
|
|
16
|
+
//! reconciled as a downstream consequence of the re-render, never the source
|
|
17
|
+
//! of truth (see `ClosePanel`).
|
|
18
|
+
//!
|
|
19
|
+
//! [`Workspace`]: crate::workspace::Workspace
|
|
20
|
+
|
|
21
|
+
use perspective_js::utils::*;
|
|
22
|
+
use yew::prelude::*;
|
|
23
|
+
|
|
24
|
+
use super::PerspectiveViewer;
|
|
25
|
+
use super::msg::PerspectiveViewerMsg::*;
|
|
26
|
+
use super::wiring::{
|
|
27
|
+
clear_active_callbacks, create_active_subscriptions, inject_active_callbacks,
|
|
28
|
+
subscribe_panel_titles,
|
|
29
|
+
};
|
|
30
|
+
use crate::config::{TableUpdate, ViewerConfigUpdate};
|
|
31
|
+
use crate::queries::*;
|
|
32
|
+
use crate::renderer::Renderer;
|
|
33
|
+
use crate::session::*;
|
|
34
|
+
use crate::tasks::*;
|
|
35
|
+
use crate::utils::{Completion, spawn_owned};
|
|
36
|
+
use crate::workspace::PanelId;
|
|
37
|
+
|
|
38
|
+
impl PerspectiveViewer {
|
|
39
|
+
pub(super) fn on_layout_changed(&mut self, ctx: &Context<Self>) -> bool {
|
|
40
|
+
self._title_subscriptions = subscribe_panel_titles(ctx);
|
|
41
|
+
self.resync_active(ctx);
|
|
42
|
+
true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// Level-triggered binding sync: re-point the root's engine bindings at
|
|
46
|
+
/// the model's *current* active panel (or the detached empty engines at
|
|
47
|
+
/// zero panels) when they differ, by `Rc` identity. Catches model-side
|
|
48
|
+
/// activations that never dispatch `SetActivePanel`.
|
|
49
|
+
fn resync_active(&mut self, ctx: &Context<Self>) -> bool {
|
|
50
|
+
let session = ctx
|
|
51
|
+
.props()
|
|
52
|
+
.workspace
|
|
53
|
+
.active_session()
|
|
54
|
+
.unwrap_or_else(|| self.empty_session.clone());
|
|
55
|
+
let renderer = ctx
|
|
56
|
+
.props()
|
|
57
|
+
.workspace
|
|
58
|
+
.active_renderer()
|
|
59
|
+
.unwrap_or_else(|| self.empty_renderer.clone());
|
|
60
|
+
|
|
61
|
+
if session == self.active_session && renderer == self.active_renderer {
|
|
62
|
+
false
|
|
63
|
+
} else {
|
|
64
|
+
self.retarget_active(ctx, session, renderer);
|
|
65
|
+
true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pub(super) fn on_set_active_panel(
|
|
70
|
+
&mut self,
|
|
71
|
+
ctx: &Context<Self>,
|
|
72
|
+
id: String,
|
|
73
|
+
completion: Option<Completion>,
|
|
74
|
+
) -> bool {
|
|
75
|
+
let id = PanelId::from(id);
|
|
76
|
+
let prev = ctx.props().workspace.active_id();
|
|
77
|
+
if prev.as_ref() == Some(&id) || !ctx.props().workspace.set_active(id.clone()) {
|
|
78
|
+
let resynced = self.resync_active(ctx);
|
|
79
|
+
if let Some(completion) = completion {
|
|
80
|
+
completion.resolve_after(async { Ok(()) });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
resynced
|
|
84
|
+
} else {
|
|
85
|
+
let new_session = ctx
|
|
86
|
+
.props()
|
|
87
|
+
.workspace
|
|
88
|
+
.active_session()
|
|
89
|
+
.unwrap_or_else(|| self.empty_session.clone());
|
|
90
|
+
|
|
91
|
+
let new_renderer = ctx
|
|
92
|
+
.props()
|
|
93
|
+
.workspace
|
|
94
|
+
.active_renderer()
|
|
95
|
+
.unwrap_or_else(|| self.empty_renderer.clone());
|
|
96
|
+
|
|
97
|
+
self.retarget_active(ctx, new_session, new_renderer);
|
|
98
|
+
let mut nudges = Vec::new();
|
|
99
|
+
for pid in prev.iter().chain(std::iter::once(&id)) {
|
|
100
|
+
if let Some(panel) = ctx.props().workspace.panel(pid)
|
|
101
|
+
&& panel.renderer.is_plugin_activated().unwrap_or(false)
|
|
102
|
+
{
|
|
103
|
+
nudges.push(activation_render(
|
|
104
|
+
panel.session.clone(),
|
|
105
|
+
panel.renderer.clone(),
|
|
106
|
+
));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let run = async move {
|
|
111
|
+
futures::future::join_all(nudges)
|
|
112
|
+
.await
|
|
113
|
+
.into_iter()
|
|
114
|
+
.collect::<ApiResult<Vec<_>>>()?;
|
|
115
|
+
Ok(())
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
match completion {
|
|
119
|
+
Some(completion) => completion.resolve_after(run),
|
|
120
|
+
None => spawn_owned("set-active-panel", run),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
true
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/// Whole-element `restore`'s single commit: the `Workspace` already holds
|
|
128
|
+
/// the final panel set (models inserted, olds ejected) and the staged
|
|
129
|
+
/// layout. Re-subscribe the per-panel title wiring (the set changed
|
|
130
|
+
/// wholesale), activate the restored panel, and re-render — `MainPanel`'s
|
|
131
|
+
/// `rendered` pass applies the staged layout and mounts every cell in
|
|
132
|
+
/// this one commit.
|
|
133
|
+
pub(super) fn on_commit_workspace_restore(&mut self, ctx: &Context<Self>, id: String) -> bool {
|
|
134
|
+
self._title_subscriptions = subscribe_panel_titles(ctx);
|
|
135
|
+
self.on_set_active_panel(ctx, id, None);
|
|
136
|
+
true
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
pub(super) fn on_close_panel(
|
|
140
|
+
&mut self,
|
|
141
|
+
ctx: &Context<Self>,
|
|
142
|
+
id: String,
|
|
143
|
+
completion: Option<Completion>,
|
|
144
|
+
) -> bool {
|
|
145
|
+
let id = PanelId::from(id);
|
|
146
|
+
|
|
147
|
+
let was_active = ctx.props().workspace.active_id().as_ref() == Some(&id);
|
|
148
|
+
let removed = ctx.props().workspace.remove_panel(&id);
|
|
149
|
+
if was_active {
|
|
150
|
+
if let Some(next) = ctx.props().workspace.panel_ids().first().cloned() {
|
|
151
|
+
ctx.props().workspace.set_active(next);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let new_session = ctx
|
|
155
|
+
.props()
|
|
156
|
+
.workspace
|
|
157
|
+
.active_session()
|
|
158
|
+
.unwrap_or_else(|| self.empty_session.clone());
|
|
159
|
+
let new_renderer = ctx
|
|
160
|
+
.props()
|
|
161
|
+
.workspace
|
|
162
|
+
.active_renderer()
|
|
163
|
+
.unwrap_or_else(|| self.empty_renderer.clone());
|
|
164
|
+
self.retarget_active(ctx, new_session, new_renderer);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let eject = removed.map(eject_panel);
|
|
168
|
+
match (eject, completion) {
|
|
169
|
+
(Some(eject), Some(completion)) => completion.resolve_after(eject),
|
|
170
|
+
(Some(eject), None) => spawn_owned("close-panel", eject),
|
|
171
|
+
(None, Some(completion)) => completion.resolve_after(async { Ok(()) }),
|
|
172
|
+
(None, None) => {},
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
apply_global_filters(&ctx.props().workspace);
|
|
176
|
+
self._title_subscriptions = subscribe_panel_titles(ctx);
|
|
177
|
+
true
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
pub(super) fn on_duplicate_panel(&mut self, ctx: &Context<Self>, id: String) -> bool {
|
|
181
|
+
if let Some(panel) = ctx.props().workspace.panel(&PanelId::from(id)) {
|
|
182
|
+
let elem = ctx.props().elem.clone();
|
|
183
|
+
let presentation = ctx.props().presentation.clone();
|
|
184
|
+
let workspace = ctx.props().workspace.clone();
|
|
185
|
+
let notify = ctx.link().callback(|_: ()| LayoutChanged);
|
|
186
|
+
let activate = ctx.link().callback(|id| SetActivePanel(id, None));
|
|
187
|
+
ApiFuture::spawn(async move {
|
|
188
|
+
let config = panel
|
|
189
|
+
.renderer
|
|
190
|
+
.clone()
|
|
191
|
+
.with_lock(async {
|
|
192
|
+
get_viewer_config(&panel.session, &panel.renderer, &presentation).await
|
|
193
|
+
})
|
|
194
|
+
.await?;
|
|
195
|
+
|
|
196
|
+
let update = ViewerConfigUpdate::decode(&config.encode()?)?;
|
|
197
|
+
let client = panel.session.get_client();
|
|
198
|
+
let new_id = create_panel(
|
|
199
|
+
&elem,
|
|
200
|
+
&presentation,
|
|
201
|
+
&workspace,
|
|
202
|
+
¬ify,
|
|
203
|
+
None,
|
|
204
|
+
update,
|
|
205
|
+
client,
|
|
206
|
+
)
|
|
207
|
+
.await?;
|
|
208
|
+
|
|
209
|
+
activate.emit(new_id.to_string());
|
|
210
|
+
Ok(())
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
false
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
pub(super) fn on_new_panel(&mut self, ctx: &Context<Self>, id: String) -> bool {
|
|
218
|
+
if let Some(panel) = ctx.props().workspace.panel(&PanelId::from(id)) {
|
|
219
|
+
let table_name = panel.session.get_table().map(|t| t.get_name().to_owned());
|
|
220
|
+
let elem = ctx.props().elem.clone();
|
|
221
|
+
let presentation = ctx.props().presentation.clone();
|
|
222
|
+
let workspace = ctx.props().workspace.clone();
|
|
223
|
+
let notify = ctx.link().callback(|_: ()| LayoutChanged);
|
|
224
|
+
let activate = ctx.link().callback(|id| SetActivePanel(id, None));
|
|
225
|
+
ApiFuture::spawn(async move {
|
|
226
|
+
let update = ViewerConfigUpdate {
|
|
227
|
+
table: table_name.map(TableUpdate::Update).unwrap_or_default(),
|
|
228
|
+
..Default::default()
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
let client = panel.session.get_client();
|
|
232
|
+
let new_id = create_panel(
|
|
233
|
+
&elem,
|
|
234
|
+
&presentation,
|
|
235
|
+
&workspace,
|
|
236
|
+
¬ify,
|
|
237
|
+
None,
|
|
238
|
+
update,
|
|
239
|
+
client,
|
|
240
|
+
)
|
|
241
|
+
.await?;
|
|
242
|
+
|
|
243
|
+
activate.emit(new_id.to_string());
|
|
244
|
+
Ok(())
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
false
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/// `NewPanelFrom` — the context menu's "New" sub-menu: a fresh
|
|
251
|
+
/// (default-config) panel bound to the named `Table` on the named
|
|
252
|
+
/// `Client`, resolved from the `Workspace` loaded-clients registry.
|
|
253
|
+
pub(super) fn on_new_panel_from(
|
|
254
|
+
&mut self,
|
|
255
|
+
ctx: &Context<Self>,
|
|
256
|
+
client_name: String,
|
|
257
|
+
table: String,
|
|
258
|
+
) -> bool {
|
|
259
|
+
let Some(client) = ctx
|
|
260
|
+
.props()
|
|
261
|
+
.workspace
|
|
262
|
+
.clients()
|
|
263
|
+
.into_iter()
|
|
264
|
+
.find(|c| c.get_name() == client_name)
|
|
265
|
+
else {
|
|
266
|
+
tracing::warn!("No loaded `Client` named \"{client_name}\"");
|
|
267
|
+
return false;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
let elem = ctx.props().elem.clone();
|
|
271
|
+
let presentation = ctx.props().presentation.clone();
|
|
272
|
+
let workspace = ctx.props().workspace.clone();
|
|
273
|
+
let notify = ctx.link().callback(|_: ()| LayoutChanged);
|
|
274
|
+
let activate = ctx.link().callback(|id| SetActivePanel(id, None));
|
|
275
|
+
ApiFuture::spawn(async move {
|
|
276
|
+
let update = ViewerConfigUpdate {
|
|
277
|
+
table: TableUpdate::Update(table),
|
|
278
|
+
..Default::default()
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
let new_id = create_panel(
|
|
282
|
+
&elem,
|
|
283
|
+
&presentation,
|
|
284
|
+
&workspace,
|
|
285
|
+
¬ify,
|
|
286
|
+
None,
|
|
287
|
+
update,
|
|
288
|
+
Some(client),
|
|
289
|
+
)
|
|
290
|
+
.await?;
|
|
291
|
+
|
|
292
|
+
activate.emit(new_id.to_string());
|
|
293
|
+
Ok(())
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
false
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/// Re-point the root's per-active engine wiring + snapshots from the
|
|
300
|
+
/// current active panel to a new one: clear the old panel's direct
|
|
301
|
+
/// callbacks, set up the new panel's callbacks + subscriptions, refresh
|
|
302
|
+
/// the snapshots, and reset the in-flight render counter (which tracked
|
|
303
|
+
/// the old panel).
|
|
304
|
+
fn retarget_active(&mut self, ctx: &Context<Self>, session: Session, renderer: Renderer) {
|
|
305
|
+
clear_active_callbacks(&self.active_session, &self.active_renderer);
|
|
306
|
+
inject_active_callbacks(ctx, &session, &renderer);
|
|
307
|
+
self._active_subscriptions = create_active_subscriptions(ctx, &session, &renderer);
|
|
308
|
+
self.session_props = session.to_props();
|
|
309
|
+
self.renderer_props = renderer.to_props(None);
|
|
310
|
+
self.update_count = session.in_flight_config_runs();
|
|
311
|
+
self.active_session = session;
|
|
312
|
+
self.active_renderer = renderer;
|
|
313
|
+
let presentation = ctx.props().presentation.clone();
|
|
314
|
+
let workspace = ctx.props().workspace.clone();
|
|
315
|
+
ApiFuture::spawn(async move {
|
|
316
|
+
let default_theme = presentation.get_default_theme_name().await;
|
|
317
|
+
if let Some(renderer) = workspace.active_renderer()
|
|
318
|
+
&& let Some(theme) = renderer.theme().or(default_theme)
|
|
319
|
+
{
|
|
320
|
+
presentation.set_theme_name(Some(&theme)).await?;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
Ok(())
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|