@perspective-dev/viewer 4.5.2 → 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.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 +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/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 +211 -77
- package/dist/wasm/perspective-viewer.js +528 -180
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +34 -26
- package/dist/wasm/snippets/perspective-viewer-daeef4ceb066e6c7/inline0.js +6 -0
- package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline3.js → perspective-viewer-daeef4ceb066e6c7/inline4.js} +5 -0
- package/package.json +3 -2
- package/src/css/_context_menu.css +14 -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 +180 -70
- package/src/rust/components/column_dropdown.rs +40 -30
- package/src/rust/components/column_selector/active_column.rs +7 -18
- package/src/rust/components/column_selector/aggregate_selector.rs +4 -7
- package/src/rust/components/column_selector/config_selector.rs +33 -81
- package/src/rust/components/column_selector/filter_column.rs +5 -15
- package/src/rust/components/column_selector/inactive_column.rs +3 -8
- package/src/rust/components/column_selector/sort_column.rs +3 -6
- package/src/rust/components/column_selector.rs +5 -11
- package/src/rust/components/containers/split_panel.rs +175 -62
- package/src/rust/components/context_menu.rs +130 -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 +311 -71
- package/src/rust/components/mod.rs +4 -0
- package/src/rust/components/panel_menu.rs +486 -0
- package/src/rust/components/panel_tab.rs +505 -0
- package/src/rust/components/plugin_tab.rs +1 -1
- 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 +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 -756
- 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 +20 -1
- 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 -837
- package/src/rust/session/column_defaults_update.rs +26 -8
- package/src/rust/session/view_subscription.rs +30 -15
- package/src/rust/session.rs +753 -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/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/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/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 +4 -2
- 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 +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/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-daeef4ceb066e6c7/inline1.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline1.js → perspective-viewer-daeef4ceb066e6c7/inline2.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline2.js → perspective-viewer-daeef4ceb066e6c7/inline3.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline4.js → perspective-viewer-daeef4ceb066e6c7/inline5.js} +0 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,402 @@
|
|
|
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
|
+
//! Settings-sidebar handlers: the open/close toggle (with its presize
|
|
14
|
+
//! choreography), the deferred divider's latest-wins presize pump
|
|
15
|
+
//! (`PRESIZE_EVERYWHERE_PLAN.md` P1/P2), and the column-settings drawer. The
|
|
16
|
+
//! presize sweeps themselves live in [`crate::tasks`] (`presize_panels`).
|
|
17
|
+
|
|
18
|
+
use futures::channel::oneshot::{Sender, channel};
|
|
19
|
+
use perspective_js::utils::*;
|
|
20
|
+
use wasm_bindgen::prelude::*;
|
|
21
|
+
use yew::prelude::*;
|
|
22
|
+
|
|
23
|
+
use super::PerspectiveViewer;
|
|
24
|
+
use super::msg::PerspectiveViewerMsg::*;
|
|
25
|
+
use crate::components::font_loader::FontLoaderStatus;
|
|
26
|
+
use crate::components::settings_panel::SelectedTab;
|
|
27
|
+
use crate::config::*;
|
|
28
|
+
use crate::presentation::{ColumnLocator, ColumnSettingsTab};
|
|
29
|
+
use crate::tasks::*;
|
|
30
|
+
|
|
31
|
+
/// The settings sidebar's geometry state, folded into one field on
|
|
32
|
+
/// [`PerspectiveViewer`]: the pane/drawer width overrides, the divider
|
|
33
|
+
/// presize pump, and the open-state deltas cache.
|
|
34
|
+
#[derive(Default)]
|
|
35
|
+
pub(super) struct SettingsGeometry {
|
|
36
|
+
/// User-dragged settings-pane width (the deferred `SplitPanel`'s
|
|
37
|
+
/// controlled `size`); `None` until first dragged / after a reset.
|
|
38
|
+
pub pane_width_override: Option<i32>,
|
|
39
|
+
|
|
40
|
+
/// The selected settings-panel tab.
|
|
41
|
+
pub selected_tab: SelectedTab,
|
|
42
|
+
|
|
43
|
+
/// High-water-mark auto width reported by the settings panel.
|
|
44
|
+
pub auto_width: f64,
|
|
45
|
+
|
|
46
|
+
/// Latest-wins presize pump for the (deferred) settings divider: the
|
|
47
|
+
/// newest proposed pane width not yet presized, and whether a pump
|
|
48
|
+
/// iteration is in flight. See `SettingsDividerMove`.
|
|
49
|
+
pub divider_target: Option<i32>,
|
|
50
|
+
pub divider_pumping: bool,
|
|
51
|
+
|
|
52
|
+
/// Open-state geometry deltas `(layout_area.w − mpc.w, main_column.h −
|
|
53
|
+
/// mpc.h)`, cached at settings *close* time — used to presize panels to
|
|
54
|
+
/// their shrunk boxes BEFORE the pane mounts on the next *open* (P2).
|
|
55
|
+
/// `None` until the first close (the first-ever open is reactive).
|
|
56
|
+
pub open_deltas: Option<(f64, f64)>,
|
|
57
|
+
|
|
58
|
+
/// User-dragged column-settings drawer width override.
|
|
59
|
+
pub column_settings_width_override: Option<i32>,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impl PerspectiveViewer {
|
|
63
|
+
pub(super) fn on_toggle_settings_init(
|
|
64
|
+
&mut self,
|
|
65
|
+
ctx: &Context<Self>,
|
|
66
|
+
update: Option<SettingsUpdate>,
|
|
67
|
+
resolve: Option<Sender<ApiResult<JsValue>>>,
|
|
68
|
+
) -> bool {
|
|
69
|
+
match (update, resolve) {
|
|
70
|
+
(Some(SettingsUpdate::Missing), None) => false,
|
|
71
|
+
(Some(SettingsUpdate::Missing), Some(resolve)) => {
|
|
72
|
+
resolve.send(Ok(JsValue::UNDEFINED)).unwrap();
|
|
73
|
+
false
|
|
74
|
+
},
|
|
75
|
+
(Some(SettingsUpdate::SetDefault), resolve) => {
|
|
76
|
+
self.init_toggle_settings_task(ctx, Some(false), resolve);
|
|
77
|
+
false
|
|
78
|
+
},
|
|
79
|
+
(Some(SettingsUpdate::Update(force)), resolve) => {
|
|
80
|
+
self.init_toggle_settings_task(ctx, Some(force), resolve);
|
|
81
|
+
false
|
|
82
|
+
},
|
|
83
|
+
(None, resolve) => {
|
|
84
|
+
self.init_toggle_settings_task(ctx, None, resolve);
|
|
85
|
+
false
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pub(super) fn on_toggle_settings_complete(
|
|
91
|
+
&mut self,
|
|
92
|
+
ctx: &Context<Self>,
|
|
93
|
+
update: SettingsUpdate,
|
|
94
|
+
resolve: Sender<()>,
|
|
95
|
+
) -> bool {
|
|
96
|
+
match update {
|
|
97
|
+
SettingsUpdate::SetDefault if self.settings_open => {
|
|
98
|
+
ctx.props().presentation.set_open_column_settings(None);
|
|
99
|
+
self.settings_open = false;
|
|
100
|
+
self.on_rendered.push(resolve);
|
|
101
|
+
true
|
|
102
|
+
},
|
|
103
|
+
SettingsUpdate::Update(force) if force != self.settings_open => {
|
|
104
|
+
ctx.props().presentation.set_open_column_settings(None);
|
|
105
|
+
self.settings_open = force;
|
|
106
|
+
self.on_rendered.push(resolve);
|
|
107
|
+
true
|
|
108
|
+
},
|
|
109
|
+
_ if matches!(self.fonts.get_status(), FontLoaderStatus::Finished) => {
|
|
110
|
+
if let Err(e) = resolve.send(()) {
|
|
111
|
+
tracing::error!("toggle settings failed {:?}", e);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
false
|
|
115
|
+
},
|
|
116
|
+
_ => {
|
|
117
|
+
ctx.props().presentation.set_open_column_settings(None);
|
|
118
|
+
self.on_rendered.push(resolve);
|
|
119
|
+
true
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/// Toggle the settings, or force the settings panel either open (true) or
|
|
125
|
+
/// closed (false) explicitly. In order to reduce apparent
|
|
126
|
+
/// screen-shear, `toggle_settings()` uses a somewhat complex render
|
|
127
|
+
/// order: it first resize the plugin's `<div>` without moving it,
|
|
128
|
+
/// using `overflow: hidden` to hide the extra draw area; then,
|
|
129
|
+
/// after the _async_ drawing of the plugin is complete, it will send a
|
|
130
|
+
/// message to complete the toggle action and re-render the element with
|
|
131
|
+
/// the settings removed.
|
|
132
|
+
///
|
|
133
|
+
/// # Arguments
|
|
134
|
+
/// * `force` - Whether to explicitly set the settings panel state to
|
|
135
|
+
/// Open/Close (`Some(true)`/`Some(false)`), or to just toggle the current
|
|
136
|
+
/// state (`None`).
|
|
137
|
+
fn init_toggle_settings_task(
|
|
138
|
+
&mut self,
|
|
139
|
+
ctx: &Context<Self>,
|
|
140
|
+
force: Option<bool>,
|
|
141
|
+
sender: Option<Sender<ApiResult<JsValue>>>,
|
|
142
|
+
) {
|
|
143
|
+
let is_open = ctx.props().presentation.is_settings_open();
|
|
144
|
+
ctx.props().presentation.set_settings_before_open(!is_open);
|
|
145
|
+
match force {
|
|
146
|
+
Some(force) if is_open == force => {
|
|
147
|
+
if let Some(sender) = sender {
|
|
148
|
+
sender.send(Ok(JsValue::UNDEFINED)).unwrap();
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
Some(_) | None => {
|
|
152
|
+
let force = !is_open;
|
|
153
|
+
let callback = ctx.link().callback(move |resolve| {
|
|
154
|
+
let update = SettingsUpdate::Update(force);
|
|
155
|
+
ToggleSettingsComplete(update, resolve)
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// P2: at CLOSE time the open-state geometry is measurable —
|
|
159
|
+
// cache the deltas the next OPEN needs to presize with.
|
|
160
|
+
if is_open {
|
|
161
|
+
self.settings_geometry.open_deltas =
|
|
162
|
+
measure_settings_open_deltas(&ctx.props().elem);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let open_deltas = self.settings_geometry.open_deltas;
|
|
166
|
+
let workspace = ctx.props().workspace.clone();
|
|
167
|
+
let presentation = ctx.props().presentation.clone();
|
|
168
|
+
let elem = ctx.props().elem.clone();
|
|
169
|
+
|
|
170
|
+
ApiFuture::spawn(async move {
|
|
171
|
+
// Resize every visible plugin (not just the active one) to its
|
|
172
|
+
// new cell as the settings pane toggles. The pane is the outer
|
|
173
|
+
// SplitPanel, which emits no `before-resize`, so this is driven
|
|
174
|
+
// explicitly here. CLOSING grows the cells, so pre-size each
|
|
175
|
+
// plugin to its grown box BEFORE collapsing the pane; OPENING
|
|
176
|
+
// shrinks them, so pre-size to the shrunk box (from the deltas
|
|
177
|
+
// cached at the last close; the first-ever open has none and
|
|
178
|
+
// stays reactive) BEFORE the pane mounts — either way, one
|
|
179
|
+
// clean resize with the reactive pass after as the exactness
|
|
180
|
+
// finalizer.
|
|
181
|
+
let result: ApiResult<JsValue> = async {
|
|
182
|
+
if is_open {
|
|
183
|
+
let presents = presize_visible_panels_grown(&workspace, &elem).await;
|
|
184
|
+
let (notify, rendered) = channel::<()>();
|
|
185
|
+
callback.emit(notify);
|
|
186
|
+
presentation.set_settings_open(false);
|
|
187
|
+
rendered.await?;
|
|
188
|
+
// `notify` fires in `rendered()` (same task as the
|
|
189
|
+
// Yew DOM patch) and this future resumes as one of
|
|
190
|
+
// its microtasks — so the staged reveal below and
|
|
191
|
+
// the pane's geometry change reach the screen in a
|
|
192
|
+
// single paint.
|
|
193
|
+
presents.reveal();
|
|
194
|
+
// I6: the exactness-finalizer resize is part of
|
|
195
|
+
// what this toggle caused — await it here rather
|
|
196
|
+
// than leaving it to the ResizeObserver (whose
|
|
197
|
+
// continuous pass remains as the reactive
|
|
198
|
+
// backstop).
|
|
199
|
+
resize_visible_panels(&workspace).await;
|
|
200
|
+
} else {
|
|
201
|
+
let presents = if let Some((delta_w, delta_h)) = open_deltas {
|
|
202
|
+
presize_visible_panels_open(&workspace, &elem, delta_w, delta_h)
|
|
203
|
+
.await
|
|
204
|
+
} else {
|
|
205
|
+
StagedPresents::default()
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
let (notify, rendered) = channel::<()>();
|
|
209
|
+
callback.emit(notify);
|
|
210
|
+
presentation.set_settings_open(true);
|
|
211
|
+
rendered.await?;
|
|
212
|
+
presents.reveal();
|
|
213
|
+
resize_visible_panels(&workspace).await;
|
|
214
|
+
}
|
|
215
|
+
Ok(JsValue::UNDEFINED)
|
|
216
|
+
}
|
|
217
|
+
.await;
|
|
218
|
+
|
|
219
|
+
if let Some(sender) = sender {
|
|
220
|
+
let msg = result.ignore_view_delete();
|
|
221
|
+
sender
|
|
222
|
+
.send(msg.map(|x| x.unwrap_or(JsValue::UNDEFINED)))
|
|
223
|
+
.into_apierror()?;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
Ok(JsValue::undefined())
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
pub(super) fn on_settings_panel_size_update(&mut self, x: Option<i32>) -> bool {
|
|
233
|
+
match x {
|
|
234
|
+
Some(x) => {
|
|
235
|
+
self.settings_geometry.pane_width_override = Some(x);
|
|
236
|
+
false
|
|
237
|
+
},
|
|
238
|
+
None => {
|
|
239
|
+
self.settings_geometry.pane_width_override = None;
|
|
240
|
+
self.settings_geometry.auto_width = 0.0;
|
|
241
|
+
self.on_settings_panel_dimensions_reset.emit(());
|
|
242
|
+
true
|
|
243
|
+
},
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
pub(super) fn on_settings_divider_move(
|
|
248
|
+
&mut self,
|
|
249
|
+
ctx: &Context<Self>,
|
|
250
|
+
pane_width: i32,
|
|
251
|
+
) -> bool {
|
|
252
|
+
// Latest-wins: overwrite the pending target; start the pump if
|
|
253
|
+
// idle. Intermediate targets that arrive while a presize is in
|
|
254
|
+
// flight are dropped (mirrors `PresizeQueue`'s single queued
|
|
255
|
+
// slot) — the pane tracks the pointer at content-render rate.
|
|
256
|
+
self.settings_geometry.divider_target = Some(pane_width);
|
|
257
|
+
if !self.settings_geometry.divider_pumping {
|
|
258
|
+
self.settings_geometry.divider_pumping = true;
|
|
259
|
+
ctx.link().send_message(SettingsDividerPump);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
false
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
pub(super) fn on_settings_divider_pump(&mut self, ctx: &Context<Self>) -> bool {
|
|
266
|
+
if let Some(pane_width) = self.settings_geometry.divider_target.take() {
|
|
267
|
+
let workspace = ctx.props().workspace.clone();
|
|
268
|
+
let elem = ctx.props().elem.clone();
|
|
269
|
+
let link = ctx.link().clone();
|
|
270
|
+
ApiFuture::spawn(async move {
|
|
271
|
+
let presents =
|
|
272
|
+
presize_visible_panels_pane_width(&workspace, &elem, pane_width as f64).await;
|
|
273
|
+
link.send_message(SettingsDividerCommit(pane_width));
|
|
274
|
+
// Same task as the commit's re-render (whether Yew drained it
|
|
275
|
+
// synchronously inside `send_message` or deferred it to a
|
|
276
|
+
// microtask): the staged reveal and the pane-width geometry
|
|
277
|
+
// land in one paint.
|
|
278
|
+
presents.reveal();
|
|
279
|
+
Ok(())
|
|
280
|
+
});
|
|
281
|
+
} else {
|
|
282
|
+
self.settings_geometry.divider_pumping = false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
false
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
pub(super) fn on_settings_divider_commit(
|
|
289
|
+
&mut self,
|
|
290
|
+
ctx: &Context<Self>,
|
|
291
|
+
pane_width: i32,
|
|
292
|
+
) -> bool {
|
|
293
|
+
// Every visible panel has rendered at (approximately) its
|
|
294
|
+
// target box — NOW move the geometry: the re-render below
|
|
295
|
+
// applies this width to the deferred `SplitPanel`'s controlled
|
|
296
|
+
// `size`, in the same task as the presizes' inline clears.
|
|
297
|
+
self.settings_geometry.pane_width_override = Some(pane_width);
|
|
298
|
+
ctx.link().send_message(SettingsDividerPump);
|
|
299
|
+
true
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
pub(super) fn on_settings_divider_finish(&mut self, ctx: &Context<Self>) -> bool {
|
|
303
|
+
let workspace = ctx.props().workspace.clone();
|
|
304
|
+
ApiFuture::spawn(async move {
|
|
305
|
+
resize_visible_panels(&workspace).await;
|
|
306
|
+
Ok(())
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
false
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
pub(super) fn on_settings_panel_tab_changed(&mut self, tab: SelectedTab) -> bool {
|
|
313
|
+
let changed = tab != self.settings_geometry.selected_tab;
|
|
314
|
+
self.settings_geometry.selected_tab = tab;
|
|
315
|
+
changed
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
pub(super) fn on_settings_panel_auto_width(&mut self, w: f64) -> bool {
|
|
319
|
+
if w > self.settings_geometry.auto_width {
|
|
320
|
+
self.settings_geometry.auto_width = w;
|
|
321
|
+
true
|
|
322
|
+
} else {
|
|
323
|
+
false
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
pub(super) fn on_open_column_settings(
|
|
328
|
+
&mut self,
|
|
329
|
+
ctx: &Context<Self>,
|
|
330
|
+
locator: Option<ColumnLocator>,
|
|
331
|
+
sender: Option<Sender<()>>,
|
|
332
|
+
toggle: bool,
|
|
333
|
+
) -> bool {
|
|
334
|
+
let mut open_column_settings = ctx.props().presentation.get_open_column_settings();
|
|
335
|
+
if locator == open_column_settings.locator {
|
|
336
|
+
if toggle {
|
|
337
|
+
ctx.props().presentation.set_open_column_settings(None);
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
open_column_settings.locator.clone_from(&locator);
|
|
341
|
+
open_column_settings.tab = if matches!(locator, Some(ColumnLocator::NewExpression)) {
|
|
342
|
+
Some(ColumnSettingsTab::Attributes)
|
|
343
|
+
} else {
|
|
344
|
+
locator.as_ref().and_then(|x| {
|
|
345
|
+
x.name().map(|x| {
|
|
346
|
+
if self.session_props.is_column_active(x) {
|
|
347
|
+
ColumnSettingsTab::Style
|
|
348
|
+
} else {
|
|
349
|
+
ColumnSettingsTab::Attributes
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
})
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
ctx.props()
|
|
356
|
+
.presentation
|
|
357
|
+
.set_open_column_settings(Some(open_column_settings));
|
|
358
|
+
|
|
359
|
+
if locator.is_some() {
|
|
360
|
+
self.settings_geometry.selected_tab = SelectedTab::Query;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if let Some(sender) = sender {
|
|
365
|
+
// I6: resolve on the render commit that applies this change (the
|
|
366
|
+
// shared `on_rendered` queue, drained in `rendered()` once fonts
|
|
367
|
+
// settle), not at message-handling time.
|
|
368
|
+
self.on_rendered.push(sender);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
true
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
pub(super) fn on_column_settings_panel_size_update(&mut self, x: Option<i32>) -> bool {
|
|
375
|
+
self.settings_geometry.column_settings_width_override = x;
|
|
376
|
+
false
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
pub(super) fn on_column_settings_tab_changed(
|
|
380
|
+
&mut self,
|
|
381
|
+
ctx: &Context<Self>,
|
|
382
|
+
tab: ColumnSettingsTab,
|
|
383
|
+
) -> bool {
|
|
384
|
+
let mut open_column_settings = ctx.props().presentation.get_open_column_settings();
|
|
385
|
+
open_column_settings.tab.clone_from(&Some(tab));
|
|
386
|
+
ctx.props()
|
|
387
|
+
.presentation
|
|
388
|
+
.set_open_column_settings(Some(open_column_settings));
|
|
389
|
+
true
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/// Toggling the debug tab re-renders the settings panel only — the
|
|
393
|
+
/// `DebugPanel` populates itself (`get_viewer_config` on mount +
|
|
394
|
+
/// change subscriptions), so no plugin dispatch is owed. The old
|
|
395
|
+
/// `just_render` here relied on the pre-amendment unconditional
|
|
396
|
+
/// `Unchanged → update` arm and repainted the plugin as a side effect
|
|
397
|
+
/// (`PLUGIN_DRAW_INVARIANT_PLAN.md` amendment, migration 2).
|
|
398
|
+
pub(super) fn on_toggle_debug(&mut self, _ctx: &Context<Self>) -> bool {
|
|
399
|
+
self.debug_open = !self.debug_open;
|
|
400
|
+
true
|
|
401
|
+
}
|
|
402
|
+
}
|