@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,184 @@
|
|
|
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 small `MainPanelMsg` handlers dispatched from `MainPanel::update` —
|
|
14
|
+
//! pointer forwarding, layout-close detection, tab/active sync, and the panel
|
|
15
|
+
//! context menu. The heavier `BeforeResize` presize lives in
|
|
16
|
+
//! [`super::presize`].
|
|
17
|
+
|
|
18
|
+
use perspective_js::utils::JsValueSerdeExt;
|
|
19
|
+
use wasm_bindgen::prelude::*;
|
|
20
|
+
use yew::prelude::*;
|
|
21
|
+
|
|
22
|
+
use super::MainPanel;
|
|
23
|
+
use crate::components::panel_menu::PanelCommand;
|
|
24
|
+
use crate::js::{Layout, RegularLayout};
|
|
25
|
+
use crate::tasks::resize_callback;
|
|
26
|
+
|
|
27
|
+
impl MainPanel {
|
|
28
|
+
pub(super) fn on_pointer_event(
|
|
29
|
+
&self,
|
|
30
|
+
ctx: &Context<Self>,
|
|
31
|
+
event: web_sys::PointerEvent,
|
|
32
|
+
) -> bool {
|
|
33
|
+
if event.target().map(JsValue::from)
|
|
34
|
+
== self
|
|
35
|
+
.main_panel_ref
|
|
36
|
+
.cast::<web_sys::HtmlElement>()
|
|
37
|
+
.map(JsValue::from)
|
|
38
|
+
{
|
|
39
|
+
ctx.props().presentation.statusbar_pointer_event.emit(event);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
pub(super) fn on_layout_updated(&mut self, ctx: &Context<Self>) -> bool {
|
|
46
|
+
// A panel still tracked in `inserted` but no longer present in
|
|
47
|
+
// the layout (calculatePath → null) was removed by the layout —
|
|
48
|
+
// i.e. its frame's close button was pressed. Report it for
|
|
49
|
+
// disposal.
|
|
50
|
+
let Some(el) = self.layout_ref.cast::<web_sys::HtmlElement>() else {
|
|
51
|
+
return false;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// A `regular-layout-update` ends any in-flight drag (it fires on
|
|
55
|
+
// the drop, not during the drag). Drop the drag ResizeObserver(s)
|
|
56
|
+
// bound in `BeforeResize`; the post-commit `resize_callback` loop
|
|
57
|
+
// below resizes the dropped panel to its final cell.
|
|
58
|
+
self.panel_resize_observers.clear();
|
|
59
|
+
|
|
60
|
+
let layout: RegularLayout = el.unchecked_into();
|
|
61
|
+
let mut closed = Vec::new();
|
|
62
|
+
self.inserted.retain(|id| {
|
|
63
|
+
if layout.contains_panel(id) {
|
|
64
|
+
true
|
|
65
|
+
} else {
|
|
66
|
+
closed.push(id.clone());
|
|
67
|
+
false
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
for id in closed {
|
|
72
|
+
ctx.props().on_close_panel.emit(id);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// A layout change (divider drag, insert, restore) reflows the
|
|
76
|
+
// grid cells without resizing the host, so the host
|
|
77
|
+
// `ResizeObserver` never fires. Resize every activated panel's
|
|
78
|
+
// plugin to match its new cell size — same per-panel
|
|
79
|
+
// `resize_callback` the insert path uses (idempotent, and each
|
|
80
|
+
// `Renderer` debounces its own resize).
|
|
81
|
+
for id in ctx.props().panel_ids.iter() {
|
|
82
|
+
// Skip any panel regular-layout reports as absent — it's
|
|
83
|
+
// leaving the layout (a close / drag-out in flight). Spawning
|
|
84
|
+
// a resize draw for it would race its disposal, and spawned
|
|
85
|
+
// tasks can't be cancelled. App-initiated closes already drop
|
|
86
|
+
// the panel from `panel_ids` before this fires; this also
|
|
87
|
+
// covers a layout-originated removal.
|
|
88
|
+
if !layout.contains_panel(id.as_str()) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if let Some(panel) = ctx.props().workspace.panel(id)
|
|
93
|
+
&& panel.renderer.is_plugin_activated().unwrap_or(false)
|
|
94
|
+
{
|
|
95
|
+
resize_callback(&panel.session, &panel.renderer).emit(());
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Recompute which panels are hidden behind an unselected stack
|
|
100
|
+
// index. Selection changes route through `restore` and so also
|
|
101
|
+
// fire `regular-layout-update` — this is the single point of
|
|
102
|
+
// truth for tab visibility. Re-render only when it changed, so
|
|
103
|
+
// each `PanelTab` re-syncs its `visible` class.
|
|
104
|
+
let hidden = layout
|
|
105
|
+
.save()
|
|
106
|
+
.into_serde_ext::<Layout>()
|
|
107
|
+
.map(|tree| tree.hidden_slot_names().into_iter().collect())
|
|
108
|
+
.unwrap_or_default();
|
|
109
|
+
|
|
110
|
+
if hidden != self.hidden_tabs {
|
|
111
|
+
self.hidden_tabs = hidden;
|
|
112
|
+
true
|
|
113
|
+
} else {
|
|
114
|
+
false
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pub(super) fn on_tab_selected(&self, ctx: &Context<Self>, name: String) -> bool {
|
|
119
|
+
ctx.props().on_activate_panel.emit(name);
|
|
120
|
+
false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub(super) fn on_context_menu(
|
|
124
|
+
&mut self,
|
|
125
|
+
ctx: &Context<Self>,
|
|
126
|
+
id: Option<String>,
|
|
127
|
+
x: f64,
|
|
128
|
+
y: f64,
|
|
129
|
+
) -> bool {
|
|
130
|
+
// Make the right-clicked panel active so active-targeting
|
|
131
|
+
// commands (e.g. Reset) act on it; then show the menu.
|
|
132
|
+
if let Some(id) = &id {
|
|
133
|
+
ctx.props().on_activate_panel.emit(id.clone());
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
self.context_menu = Some((x, y, id));
|
|
137
|
+
true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
pub(super) fn on_close_context_menu(&mut self) -> bool {
|
|
141
|
+
self.context_menu = None;
|
|
142
|
+
true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
pub(super) fn on_command(&mut self, ctx: &Context<Self>, cmd: PanelCommand) -> bool {
|
|
146
|
+
let Some((_, _, target)) = self.context_menu.clone() else {
|
|
147
|
+
return false;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
match cmd {
|
|
151
|
+
// Maximize/Restore act on the layout element this
|
|
152
|
+
// component owns. As of regular-layout 0.6.1 both route
|
|
153
|
+
// through the presize queue (cancelable `before-resize`
|
|
154
|
+
// with the post-transition paths), so the shared
|
|
155
|
+
// `BeforeResize` gate presizes the now-visible plugin(s)
|
|
156
|
+
// — no reactive post-commit resize sweep needed.
|
|
157
|
+
PanelCommand::Maximize => {
|
|
158
|
+
if let Some(id) = target
|
|
159
|
+
&& let Some(el) = self.layout_ref.cast::<web_sys::HtmlElement>()
|
|
160
|
+
{
|
|
161
|
+
el.unchecked_ref::<RegularLayout>().maximize(&id);
|
|
162
|
+
self.maximized = Some(id);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
PanelCommand::Restore => {
|
|
166
|
+
if let Some(el) = self.layout_ref.cast::<web_sys::HtmlElement>() {
|
|
167
|
+
el.unchecked_ref::<RegularLayout>().minimize();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
self.maximized = None;
|
|
171
|
+
},
|
|
172
|
+
// The stage menu (no target panel) offers ONLY `NewFrom`, whose
|
|
173
|
+
// handler resolves the client/table purely from the loaded-clients
|
|
174
|
+
// registry — the panel id is unused there, so the empty id never
|
|
175
|
+
// reaches a panel lookup.
|
|
176
|
+
cmd => ctx
|
|
177
|
+
.props()
|
|
178
|
+
.on_panel_command
|
|
179
|
+
.emit((target.unwrap_or_default(), cmd)),
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -10,18 +10,46 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
//! `MainPanel`: the multi-panel layout host. Owns the `<regular-layout>`
|
|
14
|
+
//! element, the per-panel `<PanelTab>`s, the shared status bar, and the panel
|
|
15
|
+
//! context menu. The `Component` impl below is thin — its handlers live in the
|
|
16
|
+
//! submodules:
|
|
17
|
+
//!
|
|
18
|
+
//! - [`update`] — the small `MainPanelMsg` handlers (pointer, close detection,
|
|
19
|
+
//! tab/active sync, context menu).
|
|
20
|
+
//! - [`presize`] — the `BeforeResize` pre-size-every-plugin algorithm.
|
|
21
|
+
//! - [`reconcile`] — the `rendered` layout reconcile + per-panel theme stamp.
|
|
22
|
+
//! - [`frame_theme`] — the `rendered` frame-background mirror (each
|
|
23
|
+
//! `<regular-layout-frame>`'s panel-theme background var).
|
|
24
|
+
//! - [`render`] — the `view` (status bar, cells, tabs, menu).
|
|
25
|
+
|
|
26
|
+
mod frame_theme;
|
|
27
|
+
mod presize;
|
|
28
|
+
mod reconcile;
|
|
29
|
+
mod render;
|
|
30
|
+
mod update;
|
|
31
|
+
|
|
32
|
+
pub mod msg;
|
|
33
|
+
|
|
34
|
+
use std::collections::{HashMap, HashSet};
|
|
35
|
+
|
|
36
|
+
use perspective_js::utils::JsValueSerdeExt;
|
|
13
37
|
use wasm_bindgen::prelude::*;
|
|
14
38
|
use yew::prelude::*;
|
|
15
39
|
|
|
16
|
-
use
|
|
17
|
-
use super::
|
|
40
|
+
pub use self::msg::MainPanelMsg;
|
|
41
|
+
use super::panel_menu::PanelCommand;
|
|
18
42
|
use crate::presentation::{Presentation, PresentationProps};
|
|
19
43
|
use crate::renderer::*;
|
|
20
44
|
use crate::session::{Session, SessionProps};
|
|
21
|
-
use crate::tasks::
|
|
45
|
+
use crate::tasks::PanelResizeObserverHandle;
|
|
46
|
+
use crate::workspace::{PanelId, Workspace};
|
|
22
47
|
|
|
23
48
|
#[derive(Clone, Properties)]
|
|
24
49
|
pub struct MainPanelProps {
|
|
50
|
+
/// Toggle the settings sidebar open. Fired by a `PanelTab`'s open-settings
|
|
51
|
+
/// button (after selecting + activating that panel) — the tabs are the
|
|
52
|
+
/// only open-settings affordance.
|
|
25
53
|
pub on_settings: Callback<()>,
|
|
26
54
|
|
|
27
55
|
/// Reset callback forwarded from the root component. Fired when the user
|
|
@@ -29,6 +57,21 @@ pub struct MainPanelProps {
|
|
|
29
57
|
/// + column configs), `false` for config-only.
|
|
30
58
|
pub on_reset: Callback<bool>,
|
|
31
59
|
|
|
60
|
+
/// Fired with a panel id when its frame titlebar is pressed, to make it the
|
|
61
|
+
/// active panel.
|
|
62
|
+
pub on_activate_panel: Callback<String>,
|
|
63
|
+
|
|
64
|
+
/// Fired with a panel id when its frame's close button removes it from the
|
|
65
|
+
/// layout, so the root can dispose the panel.
|
|
66
|
+
pub on_close_panel: Callback<String>,
|
|
67
|
+
|
|
68
|
+
/// Fired with `(panel id, command)` when the panel context menu selects a
|
|
69
|
+
/// command the root executes (New/Duplicate/Reset/ToggleMaster/Close).
|
|
70
|
+
/// Maximize/Restore are handled HERE (this component owns the layout
|
|
71
|
+
/// element), and Export/Copy end-to-end by
|
|
72
|
+
/// [`PanelMenu`](super::panel_menu::PanelMenu) itself.
|
|
73
|
+
pub on_panel_command: Callback<(String, PanelCommand)>,
|
|
74
|
+
|
|
32
75
|
/// Snapshots threaded from root. Read for `has_table`, `title` here in
|
|
33
76
|
/// the panel itself; threaded wholesale to `StatusBar`/`StatusIndicator`.
|
|
34
77
|
pub session_props: SessionProps,
|
|
@@ -41,7 +84,42 @@ pub struct MainPanelProps {
|
|
|
41
84
|
/// Root-managed in-flight render counter (not engine state).
|
|
42
85
|
pub update_count: u32,
|
|
43
86
|
|
|
44
|
-
///
|
|
87
|
+
/// Ids of every layout panel, in order; one `<regular-layout>` cell is
|
|
88
|
+
/// rendered per id. Drives re-render when panels are added/removed.
|
|
89
|
+
pub panel_ids: Vec<PanelId>,
|
|
90
|
+
|
|
91
|
+
/// `(panel id, session title)` for every panel. Rendered into the
|
|
92
|
+
/// `<regular-layout>` `style` as `--regular-layout-<id>--title` custom
|
|
93
|
+
/// properties, which regular-layout's tabs display via `::before` content.
|
|
94
|
+
pub panel_titles: Vec<(String, Option<String>)>,
|
|
95
|
+
|
|
96
|
+
/// `(panel id, per-panel theme)` for every panel. A snapshot so a
|
|
97
|
+
/// per-panel theme change re-renders MainPanel — `renderer.theme()` is
|
|
98
|
+
/// interior-mutable and not otherwise observed by `eq`. Each frame inlines
|
|
99
|
+
/// its theme's `--psp-*` block only when it diverges from the host theme.
|
|
100
|
+
pub panel_themes: Vec<(String, Option<String>)>,
|
|
101
|
+
|
|
102
|
+
/// The master (filter-source) panel ids, sorted. A snapshot so a master
|
|
103
|
+
/// toggle re-renders MainPanel — the role set is interior-mutable on
|
|
104
|
+
/// `Workspace` and not otherwise observed by `eq`. Drives each tab's
|
|
105
|
+
/// broadcast badge.
|
|
106
|
+
pub panel_masters: Vec<PanelId>,
|
|
107
|
+
|
|
108
|
+
/// Element-level global filters (fed by master/detail selection), threaded
|
|
109
|
+
/// to the `StatusBar` where the global-filter chips are rendered.
|
|
110
|
+
pub global_filters: Vec<perspective_client::config::Filter>,
|
|
111
|
+
|
|
112
|
+
/// Remove the global filter at this index (a chip's × in the `StatusBar`).
|
|
113
|
+
pub on_remove_global_filter: Callback<usize>,
|
|
114
|
+
|
|
115
|
+
/// Clear all global filters (the "Clear" affordance in the `StatusBar`).
|
|
116
|
+
pub on_clear_global_filters: Callback<()>,
|
|
117
|
+
|
|
118
|
+
/// The multi-panel model, for per-panel `Renderer`/`Session` access when
|
|
119
|
+
/// reconciling `insertPanel`/`removePanel`.
|
|
120
|
+
pub workspace: Workspace,
|
|
121
|
+
|
|
122
|
+
/// State (the *active* panel's handles — for the shared status bar).
|
|
45
123
|
pub session: Session,
|
|
46
124
|
pub renderer: Renderer,
|
|
47
125
|
pub presentation: Presentation,
|
|
@@ -54,6 +132,15 @@ impl PartialEq for MainPanelProps {
|
|
|
54
132
|
&& self.presentation_props == rhs.presentation_props
|
|
55
133
|
&& self.is_settings_open == rhs.is_settings_open
|
|
56
134
|
&& self.update_count == rhs.update_count
|
|
135
|
+
&& self.panel_ids == rhs.panel_ids
|
|
136
|
+
&& self.panel_titles == rhs.panel_titles
|
|
137
|
+
&& self.panel_themes == rhs.panel_themes
|
|
138
|
+
&& self.panel_masters == rhs.panel_masters
|
|
139
|
+
&& self.global_filters == rhs.global_filters
|
|
140
|
+
&& self.workspace == rhs.workspace
|
|
141
|
+
&& self.session == rhs.session
|
|
142
|
+
&& self.renderer == rhs.renderer
|
|
143
|
+
&& self.presentation == rhs.presentation
|
|
57
144
|
}
|
|
58
145
|
}
|
|
59
146
|
|
|
@@ -61,99 +148,252 @@ impl MainPanelProps {
|
|
|
61
148
|
fn is_title(&self) -> bool {
|
|
62
149
|
self.session_props.title.is_some()
|
|
63
150
|
}
|
|
64
|
-
}
|
|
65
151
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
152
|
+
pub(super) fn effective_panel_theme(&self, id: &str) -> Option<String> {
|
|
153
|
+
self.panel_themes
|
|
154
|
+
.iter()
|
|
155
|
+
.find(|(pid, _)| pid == id)
|
|
156
|
+
.and_then(|(_, theme)| theme.clone())
|
|
157
|
+
.or_else(|| self.presentation_props.available_themes.first().cloned())
|
|
158
|
+
}
|
|
69
159
|
}
|
|
70
160
|
|
|
71
161
|
pub struct MainPanel {
|
|
72
162
|
main_panel_ref: NodeRef,
|
|
163
|
+
|
|
164
|
+
/// Ref to the `<regular-layout>` element hosting the panel cells.
|
|
165
|
+
layout_ref: NodeRef,
|
|
166
|
+
|
|
167
|
+
/// Panel slots currently placed in `layout_ref`'s grid, reconciled against
|
|
168
|
+
/// `panel_ids` on each render so we `insertPanel`/`removePanel` exactly
|
|
169
|
+
/// once per add/remove.
|
|
170
|
+
inserted: Vec<String>,
|
|
171
|
+
|
|
172
|
+
/// `regular-layout-update` listener (close detection); kept alive here and
|
|
173
|
+
/// attached to the layout element once in `rendered`.
|
|
174
|
+
_layout_update_listener: Closure<dyn FnMut(web_sys::Event)>,
|
|
175
|
+
|
|
176
|
+
/// `regular-layout-select` listener (active-panel sync); kept alive here
|
|
177
|
+
/// and attached alongside the update listener.
|
|
178
|
+
_layout_select_listener: Closure<dyn FnMut(web_sys::Event)>,
|
|
179
|
+
|
|
180
|
+
/// `regular-layout-before-resize` listener (presize hook); kept alive here
|
|
181
|
+
/// and attached alongside the others.
|
|
182
|
+
_layout_before_resize_listener: Closure<dyn FnMut(web_sys::Event)>,
|
|
183
|
+
|
|
184
|
+
/// `contextmenu` listener on the panel *container* — one stable attach
|
|
185
|
+
/// point covering the whole stage, independent of the layout reconcile;
|
|
186
|
+
/// at zero panels it opens the stage menu. Kept alive here and attached
|
|
187
|
+
/// once in `rendered`. Imperative — not a Yew `oncontextmenu` — because
|
|
188
|
+
/// the plugin body is light-DOM attached by the renderer (its DOM parent
|
|
189
|
+
/// is the host, not the frame), so Yew's delegated handler never matches
|
|
190
|
+
/// a right-click there. A native listener catches it via composed
|
|
191
|
+
/// bubbling and resolves the panel from the path.
|
|
192
|
+
_contextmenu_listener: Closure<dyn FnMut(web_sys::Event)>,
|
|
193
|
+
|
|
194
|
+
/// The `<regular-layout>` ELEMENT the layout listeners are attached to.
|
|
195
|
+
/// Compared by identity in `reconcile` — if the element is ever a
|
|
196
|
+
/// different instance (it should never be: the render keys it into a
|
|
197
|
+
/// fully-keyed sibling list precisely so Yew reuses it), the listeners
|
|
198
|
+
/// are re-attached and `inserted` is reset so the fresh (empty) layout
|
|
199
|
+
/// is repopulated instead of silently orphaning every panel. A latched
|
|
200
|
+
/// `bool` here once turned an unkeyed-diff element replacement into
|
|
201
|
+
/// "Duplicate commits into an EMPTY tree with no `before-resize`
|
|
202
|
+
/// listener": the old element left the DOM with the committed tree and
|
|
203
|
+
/// every listener, and nothing ever noticed.
|
|
204
|
+
listener_target: Option<web_sys::HtmlElement>,
|
|
205
|
+
|
|
206
|
+
/// Per-panel `ResizeObserver`s, keyed by panel id, each observing that
|
|
207
|
+
/// panel's slotted plugin element and resizing only that panel's
|
|
208
|
+
/// `Renderer`. Bound in `BeforeResize` for the DRAGGED panel only (it is
|
|
209
|
+
/// excluded from the presize paths, and the `overlay` presize is disabled);
|
|
210
|
+
/// dropped on `LayoutUpdated` (the drop).
|
|
211
|
+
panel_resize_observers: HashMap<String, PanelResizeObserverHandle>,
|
|
212
|
+
|
|
213
|
+
/// Panels currently *hidden* behind an unselected index of a tab stack,
|
|
214
|
+
/// recomputed from the layout tree on every `regular-layout-update`.
|
|
215
|
+
/// Drives each tab's `visible` prop (`PanelTab` marks the front tab of
|
|
216
|
+
/// every stack, not just the active panel). Empty until the first layout
|
|
217
|
+
/// update — every panel starts visible.
|
|
218
|
+
hidden_tabs: HashSet<String>,
|
|
219
|
+
|
|
220
|
+
/// Open context menu as `(client x, client y, target panel id)`; outer
|
|
221
|
+
/// `None` when closed. A `None` *panel id* is the empty-stage menu (zero
|
|
222
|
+
/// panels — "New" only). Rendered as a cursor-anchored
|
|
223
|
+
/// [`PanelMenu`](super::panel_menu::PanelMenu) overlay.
|
|
224
|
+
context_menu: Option<(f64, f64, Option<String>)>,
|
|
225
|
+
|
|
226
|
+
/// Id of the currently maximized panel (via `regular-layout.maximize`), or
|
|
227
|
+
/// `None`. Transient (regular-layout doesn't persist it); drives the
|
|
228
|
+
/// Maximize/Restore menu label. Cleared when the panel leaves the layout.
|
|
229
|
+
maximized: Option<String>,
|
|
230
|
+
|
|
231
|
+
/// Theme-name-keyed cache of backgrounds read off stamped plugin
|
|
232
|
+
/// elements, the mirror source for frames whose own plugin is unreadable
|
|
233
|
+
/// (see [`frame_theme`]). Cleared when the theme registry changes.
|
|
234
|
+
theme_backgrounds: HashMap<String, String>,
|
|
235
|
+
|
|
236
|
+
/// The inputs `stamp_frame_themes` last mirrored from; unchanged inputs
|
|
237
|
+
/// skip the pass (and its forced style recalcs) on unrelated re-renders.
|
|
238
|
+
/// `None` until the first *fully-resolved* pass — an unresolved frame
|
|
239
|
+
/// leaves it unlatched so the mirror retries each render.
|
|
240
|
+
stamped_frame_themes: Option<frame_theme::FrameThemeSnapshot>,
|
|
241
|
+
|
|
242
|
+
/// `Workspace::staged_changed` → [`MainPanelMsg::StagedChanged`] on THIS
|
|
243
|
+
/// component's scope (see the message doc for why not the root's).
|
|
244
|
+
_staged_sub: crate::utils::Subscription,
|
|
73
245
|
}
|
|
74
246
|
|
|
75
247
|
impl Component for MainPanel {
|
|
76
248
|
type Message = MainPanelMsg;
|
|
77
249
|
type Properties = MainPanelProps;
|
|
78
250
|
|
|
79
|
-
fn create(
|
|
251
|
+
fn create(ctx: &Context<Self>) -> Self {
|
|
252
|
+
let cb = ctx.link().callback(|_: ()| MainPanelMsg::LayoutUpdated);
|
|
253
|
+
let listener = Closure::wrap(
|
|
254
|
+
Box::new(move |_: web_sys::Event| cb.emit(())) as Box<dyn FnMut(web_sys::Event)>
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
let select_cb = ctx.link().callback(MainPanelMsg::TabSelected);
|
|
258
|
+
let select_listener = Closure::wrap(Box::new(move |event: web_sys::Event| {
|
|
259
|
+
#[derive(serde::Deserialize)]
|
|
260
|
+
struct SelectDetail {
|
|
261
|
+
name: String,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if let Some(custom) = event.dyn_ref::<web_sys::CustomEvent>()
|
|
265
|
+
&& let Ok(SelectDetail { name }) = custom.detail().into_serde_ext()
|
|
266
|
+
{
|
|
267
|
+
select_cb.emit(name);
|
|
268
|
+
}
|
|
269
|
+
}) as Box<dyn FnMut(web_sys::Event)>);
|
|
270
|
+
|
|
271
|
+
// `preventDefault()` synchronously suspends the layout's resize commit
|
|
272
|
+
// (the event is cancelable); the component then pre-sizes each panel and
|
|
273
|
+
// calls `resumeResize` to release it (see `MainPanelMsg::BeforeResize`).
|
|
274
|
+
let before_resize_cb = ctx.link().callback(MainPanelMsg::BeforeResize);
|
|
275
|
+
let before_resize_listener = Closure::wrap(Box::new(move |event: web_sys::Event| {
|
|
276
|
+
event.prevent_default();
|
|
277
|
+
before_resize_cb.emit(event);
|
|
278
|
+
}) as Box<dyn FnMut(web_sys::Event)>);
|
|
279
|
+
|
|
280
|
+
// Imperative `contextmenu` listener: a right-click anywhere in a panel
|
|
281
|
+
// (the plugin body included) opens the panel menu. The plugin body is
|
|
282
|
+
// light-DOM attached by the renderer, so a Yew `oncontextmenu` on the
|
|
283
|
+
// frame never matches it (Yew walks the vdom, not the composed path).
|
|
284
|
+
// This native listener resolves the panel from the
|
|
285
|
+
// `<regular-layout-frame name=…>` on the event's composed path, then
|
|
286
|
+
// suppresses the browser menu and emits. On the EMPTY stage (zero
|
|
287
|
+
// panels — the persistent `<regular-layout>` has no frame
|
|
288
|
+
// descendants), it opens the stage menu instead, whose "New" items
|
|
289
|
+
// create the first panel.
|
|
290
|
+
let contextmenu_cb = ctx
|
|
291
|
+
.link()
|
|
292
|
+
.callback(|(id, x, y)| MainPanelMsg::ContextMenu(id, x, y));
|
|
293
|
+
let contextmenu_listener = Closure::wrap(Box::new(move |event: web_sys::Event| {
|
|
294
|
+
let path = event.composed_path();
|
|
295
|
+
let mut panel_id = None;
|
|
296
|
+
for i in 0..path.length() {
|
|
297
|
+
let node = path.get(i);
|
|
298
|
+
if let Some(el) = node.dyn_ref::<web_sys::Element>()
|
|
299
|
+
&& el.tag_name().eq_ignore_ascii_case("regular-layout-frame")
|
|
300
|
+
&& let Some(name) = el.get_attribute("name")
|
|
301
|
+
{
|
|
302
|
+
panel_id = Some(name);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let is_empty_stage = || {
|
|
308
|
+
event
|
|
309
|
+
.current_target()
|
|
310
|
+
.and_then(|t| t.dyn_into::<web_sys::Element>().ok())
|
|
311
|
+
.is_some_and(|el| {
|
|
312
|
+
el.query_selector("regular-layout-frame")
|
|
313
|
+
.ok()
|
|
314
|
+
.flatten()
|
|
315
|
+
.is_none()
|
|
316
|
+
})
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
if let Some(id) = panel_id {
|
|
320
|
+
event.prevent_default();
|
|
321
|
+
let mouse = event.unchecked_ref::<web_sys::MouseEvent>();
|
|
322
|
+
contextmenu_cb.emit((Some(id), mouse.client_x() as f64, mouse.client_y() as f64));
|
|
323
|
+
} else if is_empty_stage() {
|
|
324
|
+
event.prevent_default();
|
|
325
|
+
let mouse = event.unchecked_ref::<web_sys::MouseEvent>();
|
|
326
|
+
contextmenu_cb.emit((None, mouse.client_x() as f64, mouse.client_y() as f64));
|
|
327
|
+
}
|
|
328
|
+
// With panels present, a click outside every frame lets the
|
|
329
|
+
// native menu through.
|
|
330
|
+
}) as Box<dyn FnMut(web_sys::Event)>);
|
|
331
|
+
|
|
332
|
+
let staged_sub = {
|
|
333
|
+
use crate::utils::AddListener;
|
|
334
|
+
let cb = ctx.link().callback(|_: ()| MainPanelMsg::StagedChanged);
|
|
335
|
+
ctx.props()
|
|
336
|
+
.workspace
|
|
337
|
+
.staged_changed()
|
|
338
|
+
.add_listener(move |()| cb.emit(()))
|
|
339
|
+
};
|
|
340
|
+
|
|
80
341
|
Self {
|
|
81
342
|
main_panel_ref: NodeRef::default(),
|
|
343
|
+
layout_ref: NodeRef::default(),
|
|
344
|
+
inserted: Vec::new(),
|
|
345
|
+
_layout_update_listener: listener,
|
|
346
|
+
_layout_select_listener: select_listener,
|
|
347
|
+
_layout_before_resize_listener: before_resize_listener,
|
|
348
|
+
_contextmenu_listener: contextmenu_listener,
|
|
349
|
+
listener_target: None,
|
|
350
|
+
panel_resize_observers: HashMap::new(),
|
|
351
|
+
hidden_tabs: HashSet::new(),
|
|
352
|
+
context_menu: None,
|
|
353
|
+
maximized: None,
|
|
354
|
+
theme_backgrounds: HashMap::new(),
|
|
355
|
+
stamped_frame_themes: None,
|
|
356
|
+
_staged_sub: staged_sub,
|
|
82
357
|
}
|
|
83
358
|
}
|
|
84
359
|
|
|
85
360
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
86
361
|
match msg {
|
|
87
|
-
MainPanelMsg::PointerEvent(event) =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
false
|
|
98
|
-
},
|
|
362
|
+
MainPanelMsg::PointerEvent(event) => self.on_pointer_event(ctx, event),
|
|
363
|
+
MainPanelMsg::StagedChanged => true,
|
|
364
|
+
MainPanelMsg::LayoutUpdated => self.on_layout_updated(ctx),
|
|
365
|
+
MainPanelMsg::TabSelected(name) => self.on_tab_selected(ctx, name),
|
|
366
|
+
MainPanelMsg::ContextMenu(id, x, y) => self.on_context_menu(ctx, id, x, y),
|
|
367
|
+
MainPanelMsg::CloseContextMenu => self.on_close_context_menu(),
|
|
368
|
+
MainPanelMsg::Command(cmd) => self.on_command(ctx, cmd),
|
|
369
|
+
MainPanelMsg::BeforeResize(event) => self.on_before_resize(ctx, event),
|
|
99
370
|
}
|
|
100
371
|
}
|
|
101
372
|
|
|
102
|
-
fn changed(&mut self,
|
|
103
|
-
|
|
373
|
+
fn changed(&mut self, ctx: &Context<Self>, old: &Self::Properties) -> bool {
|
|
374
|
+
ctx.props() != old
|
|
104
375
|
}
|
|
105
376
|
|
|
106
|
-
fn
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
let mut class = classes!();
|
|
118
|
-
if !is_settings_open {
|
|
119
|
-
class.push("settings-closed");
|
|
377
|
+
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
|
|
378
|
+
// The `contextmenu` listener attaches to the panel CONTAINER — one
|
|
379
|
+
// stable attach point covering the whole stage, decoupled from the
|
|
380
|
+
// layout reconcile. Panel right-clicks bubble to it through the
|
|
381
|
+
// layout on the composed path; at zero panels it serves the stage
|
|
382
|
+
// menu.
|
|
383
|
+
if first_render && let Some(el) = self.main_panel_ref.cast::<web_sys::HtmlElement>() {
|
|
384
|
+
let _ = el.add_event_listener_with_callback(
|
|
385
|
+
"contextmenu",
|
|
386
|
+
self._contextmenu_listener.as_ref().unchecked_ref(),
|
|
387
|
+
);
|
|
120
388
|
}
|
|
121
389
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
390
|
+
self.size_staging_wrappers();
|
|
391
|
+
self.reconcile(ctx);
|
|
392
|
+
self.stamp_frame_themes(ctx);
|
|
393
|
+
}
|
|
125
394
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
html! {
|
|
130
|
-
<div id="main_column">
|
|
131
|
-
<StatusBar
|
|
132
|
-
id="status_bar"
|
|
133
|
-
{on_settings}
|
|
134
|
-
on_reset={ctx.props().on_reset.clone()}
|
|
135
|
-
session_props={ctx.props().session_props.clone()}
|
|
136
|
-
presentation_props={ctx.props().presentation_props.clone()}
|
|
137
|
-
is_settings_open={ctx.props().is_settings_open}
|
|
138
|
-
update_count={ctx.props().update_count}
|
|
139
|
-
{presentation}
|
|
140
|
-
{renderer}
|
|
141
|
-
{session}
|
|
142
|
-
/>
|
|
143
|
-
<div
|
|
144
|
-
id="main_panel_container"
|
|
145
|
-
ref={self.main_panel_ref.clone()}
|
|
146
|
-
{class}
|
|
147
|
-
onpointerdown={pointerdown}
|
|
148
|
-
>
|
|
149
|
-
<RenderWarning
|
|
150
|
-
on_dismiss={on_dismiss_warning}
|
|
151
|
-
dimensions={ctx.props().renderer_props.render_limits}
|
|
152
|
-
/>
|
|
153
|
-
<slot />
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
}
|
|
395
|
+
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
396
|
+
self.render(ctx)
|
|
157
397
|
}
|
|
158
398
|
|
|
159
399
|
fn destroy(&mut self, _ctx: &Context<Self>) {}
|
|
@@ -18,6 +18,7 @@ pub mod column_dropdown;
|
|
|
18
18
|
pub mod column_selector;
|
|
19
19
|
pub mod column_settings_sidebar;
|
|
20
20
|
pub mod containers;
|
|
21
|
+
pub mod context_menu;
|
|
21
22
|
pub mod copy_dropdown;
|
|
22
23
|
pub mod datetime_column_style;
|
|
23
24
|
pub mod editable_header;
|
|
@@ -28,9 +29,12 @@ pub mod filter_dropdown;
|
|
|
28
29
|
pub mod font_loader;
|
|
29
30
|
pub mod form;
|
|
30
31
|
pub mod function_dropdown;
|
|
32
|
+
pub mod global_filter_bar;
|
|
31
33
|
pub mod main_panel;
|
|
32
34
|
pub mod modal;
|
|
33
35
|
pub mod number_series_style;
|
|
36
|
+
pub mod panel_menu;
|
|
37
|
+
pub mod panel_tab;
|
|
34
38
|
pub mod plugin_selector;
|
|
35
39
|
pub mod plugin_tab;
|
|
36
40
|
pub mod portal;
|
|
@@ -14,10 +14,8 @@ use web_sys::{HtmlInputElement, InputEvent};
|
|
|
14
14
|
use yew::prelude::*;
|
|
15
15
|
|
|
16
16
|
use super::modal::{ModalLink, SetModalLink};
|
|
17
|
-
use super::style::LocalStyle;
|
|
18
17
|
use crate::components::form::select_enum_field::SelectEnumField;
|
|
19
18
|
use crate::config::*;
|
|
20
|
-
use crate::css;
|
|
21
19
|
use crate::utils::WeakScope;
|
|
22
20
|
|
|
23
21
|
#[derive(Properties)]
|
|
@@ -125,7 +123,6 @@ impl Component for NumberSeriesStyle {
|
|
|
125
123
|
|
|
126
124
|
html! {
|
|
127
125
|
<>
|
|
128
|
-
<LocalStyle href={css!("column-style")} />
|
|
129
126
|
<div id="column-style-container" class="number-series-style-container">
|
|
130
127
|
<SelectEnumField<ChartType>
|
|
131
128
|
label="chart-type"
|