@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.
- 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/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.cdn.d.ts +3 -0
- package/dist/esm/perspective-viewer.d.ts +9 -0
- package/dist/esm/perspective-viewer.inline.d.ts +3 -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/OnUpdateData.d.ts +6 -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 +39 -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/ViewOnUpdateResp.d.ts +4 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -2
- 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 +213 -79
- package/dist/wasm/perspective-viewer.js +537 -189
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +36 -28
- package/dist/wasm/snippets/perspective-viewer-daeef4ceb066e6c7/inline0.js +6 -0
- package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline3.js → perspective-viewer-daeef4ceb066e6c7/inline4.js} +5 -0
- package/package.json +3 -2
- package/src/css/_column_dropdown.css +14 -0
- package/src/css/_context_menu.css +14 -0
- package/src/css/_dropdown_menu.css +14 -0
- package/src/css/_filter_dropdown.css +14 -0
- package/src/css/_function_dropdown.css +14 -0
- package/src/css/_viewer_bundle.css +41 -0
- package/src/css/column-settings-panel.css +1 -1
- package/src/css/containers/context-menu.css +101 -0
- 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 +181 -71
- package/src/rust/components/column_dropdown.rs +43 -33
- package/src/rust/components/column_selector/active_column.rs +7 -18
- package/src/rust/components/column_selector/aggregate_selector.rs +4 -10
- package/src/rust/components/column_selector/config_selector.rs +33 -84
- package/src/rust/components/column_selector/empty_column.rs +0 -3
- package/src/rust/components/column_selector/filter_column.rs +5 -18
- package/src/rust/components/column_selector/inactive_column.rs +3 -8
- package/src/rust/components/column_selector/invalid_column.rs +0 -4
- package/src/rust/components/column_selector/sort_column.rs +3 -6
- package/src/rust/components/column_selector.rs +5 -14
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +0 -3
- package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +0 -3
- package/src/rust/components/column_settings_sidebar.rs +0 -3
- package/src/rust/components/containers/dropdown_menu.rs +1 -3
- package/src/rust/components/containers/scroll_panel.rs +1 -3
- package/src/rust/components/containers/split_panel.rs +175 -66
- package/src/rust/components/containers/tab_list.rs +0 -3
- package/src/rust/components/context_menu.rs +130 -0
- package/src/rust/components/datetime_column_style.rs +0 -3
- package/src/rust/components/empty_row.rs +0 -3
- package/src/rust/components/expression_editor.rs +2 -3
- package/src/rust/components/filter_dropdown.rs +3 -3
- package/src/rust/components/font_loader.rs +2 -2
- package/src/rust/components/form/code_editor.rs +0 -3
- package/src/rust/components/form/debug.rs +3 -4
- package/src/rust/components/function_dropdown.rs +3 -3
- 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 +311 -71
- package/src/rust/components/mod.rs +4 -0
- package/src/rust/components/number_series_style.rs +0 -3
- package/src/rust/components/panel_menu.rs +486 -0
- package/src/rust/components/panel_tab.rs +505 -0
- package/src/rust/components/plugin_selector.rs +0 -3
- package/src/rust/components/plugin_tab.rs +1 -6
- package/src/rust/components/portal.rs +5 -2
- package/src/rust/components/render_warning.rs +0 -3
- package/src/rust/components/settings_panel.rs +13 -5
- package/src/rust/components/status_bar.rs +113 -180
- package/src/rust/components/status_indicator.rs +6 -9
- package/src/rust/components/string_column_style.rs +0 -3
- package/src/rust/components/style/mod.rs +65 -27
- package/src/rust/components/style/style_provider.rs +51 -16
- package/src/rust/components/style_controls/number_string_format.rs +1 -8
- package/src/rust/components/type_icon.rs +1 -4
- package/src/rust/components/viewer/filters.rs +133 -0
- package/src/rust/components/viewer/msg.rs +180 -0
- package/src/rust/components/viewer/panels.rs +326 -0
- package/src/rust/components/viewer/render.rs +243 -0
- package/src/rust/components/viewer/settings.rs +402 -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 +243 -758
- 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 +9 -3
- package/src/rust/custom_elements/debug_plugin.rs +20 -1
- package/src/rust/custom_elements/export_dropdown.rs +9 -3
- 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 +20 -2
- package/src/rust/presentation.rs +66 -33
- 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 +436 -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 +350 -827
- package/src/rust/session/column_defaults_update.rs +26 -8
- package/src/rust/session/metadata.rs +11 -1
- package/src/rust/session/view_subscription.rs +30 -15
- package/src/rust/session.rs +753 -251
- 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/eject.rs +64 -23
- package/src/rust/tasks/mod.rs +28 -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/completion.rs +56 -0
- package/src/rust/utils/custom_element.rs +5 -0
- package/src/rust/utils/debounce.rs +130 -7
- package/src/rust/utils/mod.rs +6 -0
- package/src/rust/utils/pending_effects.rs +62 -0
- package/src/rust/{components/style/local_style.rs → utils/spawn.rs} +17 -19
- 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/themes/botanical.css +7 -2
- package/src/themes/defaults.css +54 -2
- package/src/themes/dracula.css +8 -3
- package/src/themes/gruvbox-dark.css +7 -2
- package/src/themes/gruvbox.css +5 -2
- package/src/themes/icons.css +4 -2
- package/src/themes/monokai.css +7 -2
- package/src/themes/phosphor.css +7 -2
- package/src/themes/pro-dark.css +7 -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 +7 -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 +15 -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 +6 -2
- package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
- package/tsconfig.json +1 -1
- package/src/rust/components/style/style_cache.rs +0 -136
- 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-3cd58f0374935772/inline0.js → perspective-viewer-daeef4ceb066e6c7/inline1.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline1.js → perspective-viewer-daeef4ceb066e6c7/inline2.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline2.js → perspective-viewer-daeef4ceb066e6c7/inline3.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-3cd58f0374935772/inline4.js → perspective-viewer-daeef4ceb066e6c7/inline5.js} +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
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 `view()`: composition of the settings pane, the
|
|
14
|
+
//! column-settings drawer, and the multi-panel `MainPanel`.
|
|
15
|
+
//!
|
|
16
|
+
//! INVARIANT: the `SplitPanel` is ALWAYS rendered with `main_panel` as its
|
|
17
|
+
//! last (flex-fill) pane — toggling the settings sidebar only adds/removes the
|
|
18
|
+
//! leading pane, never reparents `main_panel` (a remount would tear down
|
|
19
|
+
//! `<regular-layout>` + the plugin `<slot>`s).
|
|
20
|
+
|
|
21
|
+
use yew::prelude::*;
|
|
22
|
+
|
|
23
|
+
use super::PerspectiveViewer;
|
|
24
|
+
use super::msg::PerspectiveViewerMsg::*;
|
|
25
|
+
use crate::components::column_settings_sidebar::ColumnSettingsPanel;
|
|
26
|
+
use crate::components::containers::split_panel::SplitPanel;
|
|
27
|
+
use crate::components::font_loader::FontLoader;
|
|
28
|
+
use crate::components::main_panel::MainPanel;
|
|
29
|
+
use crate::components::panel_menu::PanelCommand;
|
|
30
|
+
use crate::components::settings_panel::SettingsPanel;
|
|
31
|
+
use crate::components::style::StyleProvider;
|
|
32
|
+
use crate::queries::*;
|
|
33
|
+
use crate::session::TableLoadState;
|
|
34
|
+
|
|
35
|
+
impl PerspectiveViewer {
|
|
36
|
+
pub(super) fn render(&self, ctx: &Context<Self>) -> Html {
|
|
37
|
+
// The settings panel + status bar bind to the *active* panel's engines;
|
|
38
|
+
// `presentation` is element-level (shared).
|
|
39
|
+
let presentation = &ctx.props().presentation;
|
|
40
|
+
let renderer = &self.active_renderer;
|
|
41
|
+
let session = &self.active_session;
|
|
42
|
+
|
|
43
|
+
let is_settings_open = self.settings_open
|
|
44
|
+
&& matches!(self.session_props.has_table, Some(TableLoadState::Loaded));
|
|
45
|
+
|
|
46
|
+
let mut class = classes!();
|
|
47
|
+
if !is_settings_open {
|
|
48
|
+
class.push("settings-closed");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if self.session_props.title.is_some() {
|
|
52
|
+
class.push("titled");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let on_open_expr_panel = ctx.link().callback(|c| OpenColumnSettings {
|
|
56
|
+
locator: c,
|
|
57
|
+
sender: None,
|
|
58
|
+
toggle: true,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
let on_column_settings_panel_resize = ctx
|
|
62
|
+
.link()
|
|
63
|
+
.callback(|(x, _)| ColumnSettingsPanelSizeUpdate(Some(x)));
|
|
64
|
+
|
|
65
|
+
let on_close_settings = ctx.link().callback(|()| ToggleSettingsInit(None, None));
|
|
66
|
+
let on_debug = ctx.link().callback(|_| ToggleDebug);
|
|
67
|
+
let selected_column = get_current_column_locator(
|
|
68
|
+
&self.presentation_props.open_column_settings,
|
|
69
|
+
renderer,
|
|
70
|
+
&self.session_props.config,
|
|
71
|
+
&self.session_props.metadata,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
let selected_tab = self.presentation_props.open_column_settings.tab;
|
|
75
|
+
let plugin_name = self.renderer_props.plugin_name.clone();
|
|
76
|
+
let available_plugins = self.renderer_props.available_plugins.clone();
|
|
77
|
+
let has_table = self.session_props.has_table.clone();
|
|
78
|
+
let named_column_count = self.renderer_props.config.config_column_names.len();
|
|
79
|
+
|
|
80
|
+
let view_config = self.session_props.config.clone();
|
|
81
|
+
let drag_column = self.dragdrop_props.column.clone();
|
|
82
|
+
let metadata = self.session_props.metadata.clone();
|
|
83
|
+
let on_select_tab = ctx.link().callback(SettingsPanelTabChanged);
|
|
84
|
+
let on_auto_width = ctx.link().callback(SettingsPanelAutoWidth);
|
|
85
|
+
let settings_panel = if is_settings_open {
|
|
86
|
+
html! {
|
|
87
|
+
<SettingsPanel
|
|
88
|
+
on_close={on_close_settings}
|
|
89
|
+
on_resize={&self.on_resize}
|
|
90
|
+
on_select_column={on_open_expr_panel}
|
|
91
|
+
is_debug={self.debug_open}
|
|
92
|
+
{on_debug}
|
|
93
|
+
{plugin_name}
|
|
94
|
+
{available_plugins}
|
|
95
|
+
{has_table}
|
|
96
|
+
{named_column_count}
|
|
97
|
+
{view_config}
|
|
98
|
+
plugin_config={self.renderer_props.plugin_config.clone()}
|
|
99
|
+
{drag_column}
|
|
100
|
+
metadata={metadata.clone()}
|
|
101
|
+
open_column_settings={self.presentation_props.open_column_settings.clone()}
|
|
102
|
+
selected_theme={self.presentation_props.selected_theme.clone()}
|
|
103
|
+
selected_tab={self.settings_geometry.selected_tab}
|
|
104
|
+
auto_width={self.settings_geometry.auto_width}
|
|
105
|
+
on_dimensions_reset={&self.on_settings_panel_dimensions_reset}
|
|
106
|
+
{on_select_tab}
|
|
107
|
+
{on_auto_width}
|
|
108
|
+
{presentation}
|
|
109
|
+
{renderer}
|
|
110
|
+
{session}
|
|
111
|
+
/>
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
html! { <></> }
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let on_settings = ctx.link().callback(|()| ToggleSettingsInit(None, None));
|
|
118
|
+
let on_select_tab = ctx.link().callback(ColumnSettingsTabChanged);
|
|
119
|
+
let column_settings_panel = html! {
|
|
120
|
+
if let Some(selected_column) = selected_column {
|
|
121
|
+
<SplitPanel
|
|
122
|
+
id="modal_panel"
|
|
123
|
+
reverse=true
|
|
124
|
+
initial_size={self.settings_geometry.column_settings_width_override}
|
|
125
|
+
on_reset={ctx.link().callback(|_| ColumnSettingsPanelSizeUpdate(None))}
|
|
126
|
+
on_resize={on_column_settings_panel_resize}
|
|
127
|
+
>
|
|
128
|
+
<ColumnSettingsPanel
|
|
129
|
+
{selected_column}
|
|
130
|
+
{selected_tab}
|
|
131
|
+
on_close={self.on_close_column_settings.clone()}
|
|
132
|
+
width_override={self.settings_geometry.column_settings_width_override}
|
|
133
|
+
{on_select_tab}
|
|
134
|
+
plugin_name={self.renderer_props.plugin_name.clone()}
|
|
135
|
+
{metadata}
|
|
136
|
+
view_config={self.session_props.config.clone()}
|
|
137
|
+
column_stats={self.session_props.column_stats.clone()}
|
|
138
|
+
selected_theme={self.presentation_props.selected_theme.clone()}
|
|
139
|
+
{presentation}
|
|
140
|
+
{renderer}
|
|
141
|
+
{session}
|
|
142
|
+
/>
|
|
143
|
+
<></>
|
|
144
|
+
</SplitPanel>
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
let on_reset = ctx.link().callback(|all| ResetPanel(None, all, None));
|
|
149
|
+
let main_panel = html! {
|
|
150
|
+
<MainPanel
|
|
151
|
+
{on_settings}
|
|
152
|
+
{on_reset}
|
|
153
|
+
on_activate_panel={ctx.link().callback(|id| SetActivePanel(id, None))}
|
|
154
|
+
on_close_panel={ctx.link().callback(|id| ClosePanel(id, None))}
|
|
155
|
+
on_panel_command={ctx.link().batch_callback(|(id, cmd): (String, PanelCommand)| {
|
|
156
|
+
match cmd {
|
|
157
|
+
PanelCommand::New => vec![NewPanel(id)],
|
|
158
|
+
PanelCommand::NewFrom { client, table } => {
|
|
159
|
+
vec![NewPanelFrom { client, table }]
|
|
160
|
+
},
|
|
161
|
+
PanelCommand::Duplicate => vec![DuplicatePanel(id)],
|
|
162
|
+
PanelCommand::Reset => vec![ResetPanel(Some(id), false, None)],
|
|
163
|
+
PanelCommand::ToggleMaster => vec![ToggleMaster(id)],
|
|
164
|
+
PanelCommand::Close => vec![ClosePanel(id, None)],
|
|
165
|
+
PanelCommand::Maximize | PanelCommand::Restore => vec![],
|
|
166
|
+
}
|
|
167
|
+
})}
|
|
168
|
+
session_props={self.session_props.clone()}
|
|
169
|
+
renderer_props={self.renderer_props.clone()}
|
|
170
|
+
presentation_props={self.presentation_props.clone()}
|
|
171
|
+
{is_settings_open}
|
|
172
|
+
update_count={self.update_count}
|
|
173
|
+
panel_ids={ctx.props().workspace.panel_ids()}
|
|
174
|
+
panel_titles={ctx.props()
|
|
175
|
+
.workspace
|
|
176
|
+
.panel_ids()
|
|
177
|
+
.iter()
|
|
178
|
+
.map(|id| {
|
|
179
|
+
let title = ctx
|
|
180
|
+
.props()
|
|
181
|
+
.workspace
|
|
182
|
+
.panel(id)
|
|
183
|
+
.and_then(|p| p.session.get_title())
|
|
184
|
+
.filter(|t| !t.is_empty());
|
|
185
|
+
(id.as_str().to_owned(), title)
|
|
186
|
+
})
|
|
187
|
+
.collect::<Vec<_>>()}
|
|
188
|
+
panel_themes={ctx.props()
|
|
189
|
+
.workspace
|
|
190
|
+
.panel_ids()
|
|
191
|
+
.iter()
|
|
192
|
+
.map(|id| {
|
|
193
|
+
(
|
|
194
|
+
id.as_str().to_owned(),
|
|
195
|
+
ctx.props().workspace.panel(id).and_then(|p| p.renderer.theme()),
|
|
196
|
+
)
|
|
197
|
+
})
|
|
198
|
+
.collect::<Vec<_>>()}
|
|
199
|
+
panel_masters={ctx.props().workspace.masters()}
|
|
200
|
+
workspace={ctx.props().workspace.clone()}
|
|
201
|
+
global_filters={self.global_filters.clone()}
|
|
202
|
+
on_remove_global_filter={ctx.link().callback(RemoveGlobalFilter)}
|
|
203
|
+
on_clear_global_filters={ctx.link().callback(|_| ClearGlobalFilters)}
|
|
204
|
+
{presentation}
|
|
205
|
+
{renderer}
|
|
206
|
+
{session}
|
|
207
|
+
/>
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
let is_single_panel = if ctx.props().workspace.placed_count() == 1 {
|
|
211
|
+
"only-child"
|
|
212
|
+
} else {
|
|
213
|
+
""
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
html! {
|
|
217
|
+
<StyleProvider root={ctx.props().elem.clone()}>
|
|
218
|
+
<div id="component_container" class={is_single_panel}>
|
|
219
|
+
<div id="layout_area">
|
|
220
|
+
<SplitPanel
|
|
221
|
+
id="app_panel"
|
|
222
|
+
reverse=true
|
|
223
|
+
skip_empty=true
|
|
224
|
+
deferred=true
|
|
225
|
+
size={self.settings_geometry.pane_width_override}
|
|
226
|
+
initial_size={self.settings_geometry.pane_width_override}
|
|
227
|
+
on_reset={ctx.link().callback(|_| SettingsPanelSizeUpdate(None))}
|
|
228
|
+
on_resize={ctx.link().callback(|(x, _): (i32, i32)| SettingsDividerMove(x))}
|
|
229
|
+
on_resize_finished={ctx.link().callback(|_| SettingsDividerFinish)}
|
|
230
|
+
>
|
|
231
|
+
{ settings_panel }
|
|
232
|
+
<div id="main_column_container">
|
|
233
|
+
{ main_panel }
|
|
234
|
+
{ column_settings_panel }
|
|
235
|
+
</div>
|
|
236
|
+
</SplitPanel>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
<FontLoader ..self.fonts.clone() />
|
|
240
|
+
</StyleProvider>
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|