@perspective-dev/viewer 5.3.1 → 5.4.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 +6 -6
- package/dist/cdn/perspective-viewer.js.map +2 -2
- package/dist/css/blueprint.css +1 -1
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/ar.css +1 -0
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/ledger.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.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/css/velvet.css +1 -1
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/extensions.d.ts +0 -5
- package/dist/esm/perspective-viewer.inline.js +9 -9
- package/dist/esm/perspective-viewer.inline.js.map +3 -3
- package/dist/esm/perspective-viewer.js +5 -5
- package/dist/esm/perspective-viewer.js.map +2 -2
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
- package/dist/wasm/perspective-viewer.d.ts +62 -73
- package/dist/wasm/perspective-viewer.js +226 -238
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +18 -28
- package/package.json +7 -7
- package/src/css/_context_menu.css +1 -1
- package/src/css/_dropdown_menu.css +1 -1
- package/src/css/_viewer_bundle.css +7 -8
- package/src/css/column-dropdown.css +0 -3
- package/src/css/column-selector.css +8 -5
- package/src/css/column-settings-panel.css +0 -225
- package/src/css/column-style.css +13 -0
- package/src/css/config-selector.css +18 -10
- package/src/css/filter-dropdown.css +5 -5
- package/src/css/form/debug.css +28 -4
- package/src/css/function-dropdown.css +5 -5
- package/src/css/panel-tab.css +23 -0
- package/src/css/plugin-selector.css +5 -5
- package/src/css/render-warning.css +1 -1
- package/src/css/status-bar.css +52 -21
- package/src/css/type-icon.css +1 -3
- package/src/css/{containers → ui/containers}/context-menu.css +22 -16
- package/src/css/ui/containers/dropdown-menu-items.css +66 -0
- package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
- package/src/css/viewer.css +32 -26
- package/src/rust/components/chat_panel.rs +1 -1
- package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
- package/src/rust/components/column_dropdown.rs +2 -2
- package/src/rust/components/column_selector/active_column.rs +15 -9
- package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
- package/src/rust/components/column_selector/config_selector.rs +2 -2
- package/src/rust/components/column_selector/filter_column.rs +8 -5
- package/src/rust/components/column_selector/inactive_column.rs +4 -2
- package/src/rust/components/column_selector/pivot_column.rs +4 -3
- package/src/rust/components/column_selector/sort_column.rs +7 -4
- package/src/rust/components/column_selector.rs +3 -3
- package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +15 -17
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab.rs +4 -5
- package/src/rust/components/column_settings_sidebar.rs +3 -4
- package/src/rust/components/copy_dropdown.rs +1 -1
- package/src/rust/components/datetime_column_style/custom.rs +1 -3
- package/src/rust/components/datetime_column_style/simple.rs +1 -2
- package/src/rust/components/datetime_column_style.rs +1 -2
- package/src/rust/components/{form/debug.rs → debug_panel.rs} +5 -5
- package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
- package/src/rust/components/export_dropdown.rs +1 -1
- package/src/rust/components/expression_editor.rs +1 -1
- package/src/rust/components/filter_dropdown.rs +2 -2
- package/src/rust/components/function_dropdown.rs +2 -2
- package/src/rust/components/main_panel/frame_theme.rs +3 -59
- package/src/rust/components/main_panel/reconcile.rs +9 -4
- package/src/rust/components/main_panel/render.rs +62 -11
- package/src/rust/components/main_panel/update.rs +39 -10
- package/src/rust/components/main_panel.rs +26 -2
- package/src/rust/components/mod.rs +5 -7
- package/src/rust/components/new_panel_menu.rs +97 -0
- package/src/rust/components/number_series_style.rs +2 -3
- package/src/rust/components/panel_menu.rs +61 -87
- package/src/rust/components/panel_tab.rs +100 -43
- package/src/rust/components/plugin_tab.rs +1 -1
- package/src/rust/components/render_warning.rs +2 -2
- package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
- package/src/rust/components/settings_panel.rs +2 -2
- package/src/rust/components/status_bar.rs +113 -19
- package/src/rust/components/status_indicator.rs +5 -14
- package/src/rust/components/string_column_style.rs +1 -2
- package/src/rust/components/{style/mod.rs → style.rs} +32 -15
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +3 -5
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
- package/src/rust/components/viewer/msg.rs +43 -30
- package/src/rust/components/viewer/panels.rs +50 -19
- package/src/rust/components/viewer/render.rs +15 -13
- package/src/rust/components/viewer/settings.rs +137 -68
- package/src/rust/components/viewer/snapshots.rs +5 -10
- package/src/rust/components/viewer/wiring.rs +1 -21
- package/src/rust/components/viewer.rs +15 -10
- package/src/rust/components/window_editor.rs +8 -9
- package/src/rust/config/column_config_schema.rs +2 -27
- package/src/rust/config/mod.rs +0 -2
- package/src/rust/config/workspace_config.rs +2 -1
- package/src/rust/custom_elements/debug_plugin.rs +1 -10
- package/src/rust/custom_elements/mod.rs +0 -3
- package/src/rust/custom_elements/viewer.rs +0 -18
- package/src/rust/js/plugin.rs +0 -86
- package/src/rust/lib.rs +12 -15
- package/src/rust/presentation/column_locator.rs +3 -7
- package/src/rust/presentation.rs +68 -38
- package/src/rust/queries/hosted_tables.rs +38 -0
- package/src/rust/queries/mod.rs +2 -0
- package/src/rust/queries/palette_set.rs +1 -1
- package/src/rust/renderer/activate.rs +1 -3
- package/src/rust/renderer/dispatch.rs +83 -22
- package/src/rust/renderer/limits.rs +24 -5
- package/src/rust/renderer/plugin_config.rs +1 -0
- package/src/rust/renderer/props.rs +2 -1
- package/src/rust/renderer.rs +14 -27
- package/src/rust/session/props.rs +4 -3
- package/src/rust/session/view_subscription.rs +19 -6
- package/src/rust/session.rs +38 -70
- package/src/rust/tasks/create_panel.rs +1 -1
- package/src/rust/tasks/dismiss_render_warning.rs +1 -1
- package/src/rust/tasks/eject.rs +1 -0
- package/src/rust/tasks/pipeline.rs +0 -60
- package/src/rust/tasks/presize_panels.rs +24 -1
- package/src/rust/tasks/restore_and_render.rs +0 -17
- package/src/rust/tasks/send_column_config.rs +1 -1
- package/src/rust/tasks/send_plugin_config.rs +1 -1
- package/src/rust/tasks/set_edit_mode.rs +1 -1
- package/src/rust/{components → ui}/containers/control_group.rs +25 -3
- package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
- package/src/rust/{components → ui}/containers/mod.rs +0 -4
- package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
- package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
- package/src/rust/{components → ui}/containers/select.rs +0 -6
- package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
- package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
- package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
- package/src/rust/{components → ui}/context_menu.rs +8 -13
- package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
- package/src/rust/{components → ui}/font_loader.rs +0 -26
- package/src/rust/{components → ui}/form/color_selector.rs +4 -2
- package/src/rust/ui/form/intl_label.rs +81 -0
- package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
- package/src/rust/{components → ui}/form/mod.rs +1 -10
- package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
- package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
- package/src/rust/{components → ui}/form/number_field.rs +1 -1
- package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
- package/src/rust/{components → ui}/form/optional_field.rs +3 -1
- package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
- package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
- package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
- package/src/rust/ui/mod.rs +56 -0
- package/src/rust/ui/modal.rs +61 -0
- package/src/rust/{components → ui}/portal.rs +174 -79
- package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
- package/src/rust/utils/completion.rs +0 -6
- package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
- package/src/rust/utils/mod.rs +2 -0
- package/src/rust/workspace.rs +35 -0
- package/src/svg/boolean-type.svg +3 -2
- package/src/svg/checkbox-checked-icon.svg +4 -7
- package/src/svg/checkbox-hover.svg +3 -3
- package/src/svg/checkbox-off.svg +2 -3
- package/src/svg/checkbox-on.svg +3 -3
- package/src/svg/checkbox-unchecked-icon.svg +3 -3
- package/src/svg/datagrid-select-column.svg +8 -15
- package/src/svg/datagrid-select-region.svg +8 -15
- package/src/svg/datagrid-select-row-tree.svg +7 -12
- package/src/svg/datagrid-select-row.svg +8 -13
- package/src/svg/date-type.svg +4 -23
- package/src/svg/drawer-tab-hover.svg +4 -6
- package/src/svg/drawer-tab-invert-hover.svg +5 -5
- package/src/svg/drawer-tab-invert.svg +3 -5
- package/src/svg/drawer-tab.svg +5 -5
- package/src/svg/mega-menu-icons-candlestick.svg +5 -37
- package/src/svg/mega-menu-icons-ohlc.svg +4 -39
- package/src/svg/mega-menu-icons-sunburst.svg +4 -38
- package/src/svg/mega-menu-icons-treemap.svg +3 -43
- package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
- package/src/svg/new-panel-icon.svg +4 -0
- package/src/svg/number-type.svg +3 -3
- package/src/svg/radio-hover.svg +3 -3
- package/src/svg/radio-off.svg +3 -3
- package/src/svg/radio-on.svg +3 -3
- package/src/svg/status_ok.svg +3 -26
- package/src/svg/string-type.svg +4 -8
- package/src/svg/theme-icon.svg +5 -5
- package/src/themes/blueprint.css +0 -57
- package/src/themes/botanical.css +0 -36
- package/src/themes/defaults.css +1 -24
- package/src/themes/dracula.css +0 -58
- package/src/themes/eggplant.css +0 -58
- package/src/themes/gruvbox-dark.css +0 -65
- package/src/themes/gruvbox.css +1 -39
- package/src/themes/icons.css +1 -8
- package/src/themes/intl/ar.css +185 -0
- package/src/themes/intl/de.css +45 -35
- package/src/themes/intl/es.css +40 -30
- package/src/themes/intl/fr.css +39 -29
- package/src/themes/intl/ja.css +41 -31
- package/src/themes/intl/pt.css +37 -27
- package/src/themes/intl/zh.css +47 -37
- package/src/themes/intl.css +16 -3
- package/src/themes/ledger.css +1 -59
- package/src/themes/monokai.css +0 -51
- package/src/themes/nord.css +0 -55
- package/src/themes/phosphor.css +0 -48
- package/src/themes/pro-dark.css +0 -37
- package/src/themes/pro.css +3 -23
- package/src/themes/solarized-dark.css +0 -31
- package/src/themes/solarized.css +1 -24
- package/src/themes/vaporwave.css +0 -44
- package/src/themes/velvet.css +0 -60
- package/src/ts/extensions.ts +0 -13
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
- package/src/css/plugin-settings-panel.css +0 -171
- package/src/rust/components/modal.rs +0 -177
- package/src/rust/custom_elements/copy_dropdown.rs +0 -219
- package/src/rust/custom_elements/export_dropdown.rs +0 -216
- package/src/rust/custom_elements/modal.rs +0 -299
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-adc6f3f79141112b}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-adc6f3f79141112b}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-adc6f3f79141112b}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-adc6f3f79141112b}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-adc6f3f79141112b}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-adc6f3f79141112b}/inline5.js +0 -0
- /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
- /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
- /package/src/css/{containers → ui/containers}/tabs.css +0 -0
- /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
- /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
- /package/src/css/{dom → ui/dom}/select.css +0 -0
- /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
- /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
- /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
- /package/src/rust/{components → ui}/form/number_input.rs +0 -0
|
@@ -190,16 +190,21 @@ impl MainPanel {
|
|
|
190
190
|
self.inserted.push(name.to_owned());
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
let mut pending = std::mem::take(&mut self.pending_removals);
|
|
194
194
|
self.inserted.retain(|name| {
|
|
195
195
|
if panel_ids.iter().any(|id| id.as_str() == name) {
|
|
196
|
-
true
|
|
197
|
-
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if pending.insert(name.clone()) {
|
|
198
200
|
let _ = layout.remove_panel(name);
|
|
199
|
-
false
|
|
200
201
|
}
|
|
202
|
+
|
|
203
|
+
true
|
|
201
204
|
});
|
|
202
205
|
|
|
206
|
+
self.pending_removals = pending;
|
|
207
|
+
|
|
203
208
|
// A removed panel can't stay "maximized" (regular-layout drops the
|
|
204
209
|
// maximize stylesheet when its panel leaves the layout).
|
|
205
210
|
if let Some(m) = &self.maximized
|
|
@@ -18,7 +18,8 @@ use wasm_bindgen::prelude::*;
|
|
|
18
18
|
use yew::prelude::*;
|
|
19
19
|
|
|
20
20
|
use super::{MainPanel, MainPanelMsg, MainPanelProps};
|
|
21
|
-
use crate::components::
|
|
21
|
+
use crate::components::new_panel_menu::NewPanelPick;
|
|
22
|
+
use crate::components::panel_menu::{PanelCommand, PanelMenu};
|
|
22
23
|
use crate::components::panel_tab::PanelTab;
|
|
23
24
|
use crate::components::render_warning::RenderWarning;
|
|
24
25
|
use crate::components::status_bar::StatusBar;
|
|
@@ -73,14 +74,26 @@ impl MainPanel {
|
|
|
73
74
|
let active_slot = renderer.slot_name();
|
|
74
75
|
// Panel-count chrome (`single`/`multi`, closable, draggable)
|
|
75
76
|
let placed_count = ctx.props().workspace.placed_count();
|
|
77
|
+
let closing = self
|
|
78
|
+
.inserted
|
|
79
|
+
.iter()
|
|
80
|
+
.filter(|name| !ctx.props().panel_ids.iter().any(|id| id.as_str() == *name))
|
|
81
|
+
.map(|name| PanelId::from(name.as_str()))
|
|
82
|
+
.collect::<Vec<_>>();
|
|
76
83
|
let cells = ctx
|
|
77
84
|
.props()
|
|
78
85
|
.panel_ids
|
|
79
86
|
.iter()
|
|
80
87
|
.filter(|id| !ctx.props().workspace.is_staged(id))
|
|
81
|
-
.map(|id|
|
|
88
|
+
.map(|id| (id, false))
|
|
89
|
+
.chain(closing.iter().map(|id| (id, true)))
|
|
90
|
+
.map(|(id, is_closing)| {
|
|
82
91
|
let name = id.as_str().to_owned();
|
|
83
92
|
let mut class = classes!("rl-panel");
|
|
93
|
+
if is_closing {
|
|
94
|
+
class.push("closing");
|
|
95
|
+
}
|
|
96
|
+
|
|
84
97
|
if placed_count > 1 {
|
|
85
98
|
if active_slot.as_deref() == Some(name.as_str()) {
|
|
86
99
|
class.push("active");
|
|
@@ -103,7 +116,9 @@ impl MainPanel {
|
|
|
103
116
|
let on_activate = ctx.props().on_activate_panel.clone();
|
|
104
117
|
let activate_name = name.clone();
|
|
105
118
|
let onpointerdown = Callback::from(move |_: web_sys::PointerEvent| {
|
|
106
|
-
|
|
119
|
+
if !is_closing {
|
|
120
|
+
on_activate.emit(activate_name.clone());
|
|
121
|
+
}
|
|
107
122
|
});
|
|
108
123
|
|
|
109
124
|
// Right-click anywhere in the panel opens the context menu. This
|
|
@@ -214,23 +229,41 @@ impl MainPanel {
|
|
|
214
229
|
on_settings.emit(());
|
|
215
230
|
})
|
|
216
231
|
};
|
|
232
|
+
let closing_tabs = closing.iter().filter_map(|id| {
|
|
233
|
+
let panel = ctx.props().workspace.closing_panel(id)?;
|
|
234
|
+
let title = panel.session.get_title().filter(|t| !t.is_empty());
|
|
235
|
+
let theme = panel.renderer.theme().or_else(|| {
|
|
236
|
+
ctx.props()
|
|
237
|
+
.presentation_props
|
|
238
|
+
.available_themes
|
|
239
|
+
.first()
|
|
240
|
+
.cloned()
|
|
241
|
+
});
|
|
242
|
+
Some((id, panel.session, title, theme, true))
|
|
243
|
+
});
|
|
217
244
|
let tabs = ctx
|
|
218
245
|
.props()
|
|
219
246
|
.panel_ids
|
|
220
247
|
.iter()
|
|
221
248
|
.filter(|id| !ctx.props().workspace.is_staged(id))
|
|
222
|
-
.
|
|
223
|
-
let
|
|
249
|
+
.filter_map(|id| {
|
|
250
|
+
let panel = ctx.props().workspace.panel(id)?;
|
|
251
|
+
let name = id.as_str();
|
|
224
252
|
let title = ctx
|
|
225
253
|
.props()
|
|
226
254
|
.panel_titles
|
|
227
255
|
.iter()
|
|
228
|
-
.find(|(pid, _)| pid ==
|
|
256
|
+
.find(|(pid, _)| pid == name)
|
|
229
257
|
.and_then(|(_, t)| t.clone());
|
|
230
|
-
let
|
|
258
|
+
let theme = ctx.props().effective_panel_theme(name);
|
|
259
|
+
Some((id, panel.session, title, theme, false))
|
|
260
|
+
})
|
|
261
|
+
.chain(closing_tabs)
|
|
262
|
+
.map(|(id, session, title, theme, is_closing)| {
|
|
263
|
+
let name = id.as_str().to_owned();
|
|
264
|
+
let active = !is_closing && active_slot.as_deref() == Some(name.as_str());
|
|
231
265
|
let visible = !self.hidden_tabs.contains(name.as_str());
|
|
232
|
-
let is_master = ctx.props().panel_masters.contains(id);
|
|
233
|
-
let theme = ctx.props().effective_panel_theme(&name);
|
|
266
|
+
let is_master = !is_closing && ctx.props().panel_masters.contains(id);
|
|
234
267
|
|
|
235
268
|
html! {
|
|
236
269
|
<PanelTab
|
|
@@ -238,14 +271,16 @@ impl MainPanel {
|
|
|
238
271
|
viewer={viewer_elem.clone()}
|
|
239
272
|
panel_id={name.clone()}
|
|
240
273
|
{title}
|
|
274
|
+
{session}
|
|
241
275
|
{theme}
|
|
242
276
|
{active}
|
|
243
277
|
{visible}
|
|
244
278
|
{is_master}
|
|
245
279
|
{single}
|
|
246
|
-
{closable}
|
|
280
|
+
closable={closable && !is_closing}
|
|
247
281
|
{is_settings_open}
|
|
248
|
-
{draggable}
|
|
282
|
+
draggable={draggable && !is_closing}
|
|
283
|
+
closing={is_closing}
|
|
249
284
|
on_select={on_select.clone()}
|
|
250
285
|
on_close={on_close.clone()}
|
|
251
286
|
on_open_settings={on_open_settings.clone()}
|
|
@@ -349,11 +384,27 @@ impl MainPanel {
|
|
|
349
384
|
})
|
|
350
385
|
.unwrap_or_default();
|
|
351
386
|
|
|
387
|
+
let on_new_panel = {
|
|
388
|
+
let on_command = ctx.props().on_panel_command.clone();
|
|
389
|
+
let id = renderer.slot_name().unwrap_or_default();
|
|
390
|
+
Callback::from(move |pick: NewPanelPick| {
|
|
391
|
+
let cmd = match pick {
|
|
392
|
+
NewPanelPick::FromTable { client, table } => {
|
|
393
|
+
PanelCommand::NewFrom { client, table }
|
|
394
|
+
},
|
|
395
|
+
NewPanelPick::FromPanel(source) => PanelCommand::NewFromPanel(source),
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
on_command.emit((id.clone(), cmd));
|
|
399
|
+
})
|
|
400
|
+
};
|
|
401
|
+
|
|
352
402
|
html! {
|
|
353
403
|
<div id="main_column">
|
|
354
404
|
<StatusBar
|
|
355
405
|
id="status_bar"
|
|
356
406
|
on_reset={ctx.props().on_reset.clone()}
|
|
407
|
+
{on_new_panel}
|
|
357
408
|
session_props={ctx.props().session_props.clone()}
|
|
358
409
|
presentation_props={ctx.props().presentation_props.clone()}
|
|
359
410
|
is_settings_open={ctx.props().is_settings_open}
|
|
@@ -22,6 +22,7 @@ use yew::prelude::*;
|
|
|
22
22
|
use super::MainPanel;
|
|
23
23
|
use crate::components::panel_menu::PanelCommand;
|
|
24
24
|
use crate::js::{Layout, RegularLayout};
|
|
25
|
+
use crate::renderer::SUBPIXEL_EPSILON;
|
|
25
26
|
use crate::tasks::resize_callback;
|
|
26
27
|
|
|
27
28
|
impl MainPanel {
|
|
@@ -42,11 +43,9 @@ impl MainPanel {
|
|
|
42
43
|
false
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
/// Report closed every panel this commit dropped from the layout, and
|
|
47
|
+
/// re-render so its frame leaves the DOM in the commit's paint.
|
|
45
48
|
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
49
|
let Some(el) = self.layout_ref.cast::<web_sys::HtmlElement>() else {
|
|
51
50
|
return false;
|
|
52
51
|
};
|
|
@@ -68,10 +67,14 @@ impl MainPanel {
|
|
|
68
67
|
}
|
|
69
68
|
});
|
|
70
69
|
|
|
70
|
+
let closed_any = !closed.is_empty();
|
|
71
71
|
for id in closed {
|
|
72
|
-
|
|
72
|
+
self.pending_removals.remove(&id);
|
|
73
|
+
ctx.props().on_panel_closed.emit(id);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
let reissue = self.forget_dropped_removals(&layout);
|
|
77
|
+
|
|
75
78
|
// A layout change (divider drag, insert, restore) reflows the
|
|
76
79
|
// grid cells without resizing the host, so the host
|
|
77
80
|
// `ResizeObserver` never fires. Resize every activated panel's
|
|
@@ -92,6 +95,19 @@ impl MainPanel {
|
|
|
92
95
|
if let Some(panel) = ctx.props().workspace.panel(id)
|
|
93
96
|
&& panel.renderer.is_plugin_activated().unwrap_or(false)
|
|
94
97
|
{
|
|
98
|
+
if let Some((w, h)) = panel.renderer.take_presized_box()
|
|
99
|
+
&& let Some(plugin) = panel.renderer.active_plugin()
|
|
100
|
+
{
|
|
101
|
+
let rect = plugin
|
|
102
|
+
.unchecked_ref::<web_sys::Element>()
|
|
103
|
+
.get_bounding_client_rect();
|
|
104
|
+
if (rect.width() - w).abs() <= SUBPIXEL_EPSILON
|
|
105
|
+
&& (rect.height() - h).abs() <= SUBPIXEL_EPSILON
|
|
106
|
+
{
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
95
111
|
resize_callback(&panel.session, &panel.renderer).emit(());
|
|
96
112
|
}
|
|
97
113
|
}
|
|
@@ -111,10 +127,27 @@ impl MainPanel {
|
|
|
111
127
|
self.hidden_tabs = hidden;
|
|
112
128
|
true
|
|
113
129
|
} else {
|
|
114
|
-
|
|
130
|
+
closed_any || reissue
|
|
115
131
|
}
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
/// Forget every issued removal whose panel survived the commit, so the
|
|
135
|
+
/// next `reconcile` re-issues it.
|
|
136
|
+
fn forget_dropped_removals(&mut self, layout: &RegularLayout) -> bool {
|
|
137
|
+
let dropped = self
|
|
138
|
+
.pending_removals
|
|
139
|
+
.iter()
|
|
140
|
+
.filter(|id| layout.contains_panel(id))
|
|
141
|
+
.cloned()
|
|
142
|
+
.collect::<Vec<_>>();
|
|
143
|
+
let reissue = !dropped.is_empty();
|
|
144
|
+
for id in dropped {
|
|
145
|
+
self.pending_removals.remove(&id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
reissue
|
|
149
|
+
}
|
|
150
|
+
|
|
118
151
|
pub(super) fn on_tab_selected(&self, ctx: &Context<Self>, name: String) -> bool {
|
|
119
152
|
ctx.props().on_activate_panel.emit(name);
|
|
120
153
|
false
|
|
@@ -169,10 +202,6 @@ impl MainPanel {
|
|
|
169
202
|
|
|
170
203
|
self.maximized = None;
|
|
171
204
|
},
|
|
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
205
|
cmd => ctx
|
|
177
206
|
.props()
|
|
178
207
|
.on_panel_command
|
|
@@ -61,10 +61,14 @@ pub struct MainPanelProps {
|
|
|
61
61
|
/// active panel.
|
|
62
62
|
pub on_activate_panel: Callback<String>,
|
|
63
63
|
|
|
64
|
-
/// Fired with a panel id
|
|
65
|
-
///
|
|
64
|
+
/// Fired with a panel id from a tab × (an app-initiated close): the root
|
|
65
|
+
/// removes it from the model at once and parks its engines.
|
|
66
66
|
pub on_close_panel: Callback<String>,
|
|
67
67
|
|
|
68
|
+
/// Fired with a panel id once the `regular-layout` tree no longer holds
|
|
69
|
+
/// it, so the root can eject the parked panel.
|
|
70
|
+
pub on_panel_closed: Callback<String>,
|
|
71
|
+
|
|
68
72
|
/// Fired with `(panel id, command)` when the panel context menu selects a
|
|
69
73
|
/// command the root executes (New/Duplicate/Reset/ToggleMaster/Close).
|
|
70
74
|
/// Maximize/Restore are handled HERE (this component owns the layout
|
|
@@ -203,6 +207,10 @@ pub struct MainPanel {
|
|
|
203
207
|
/// every listener, and nothing ever noticed.
|
|
204
208
|
listener_target: Option<web_sys::HtmlElement>,
|
|
205
209
|
|
|
210
|
+
/// Ids gone from the model whose `removePanel` has been issued to the
|
|
211
|
+
/// layout and not yet committed.
|
|
212
|
+
pending_removals: HashSet<String>,
|
|
213
|
+
|
|
206
214
|
/// Per-panel `ResizeObserver`s, keyed by panel id, each observing that
|
|
207
215
|
/// panel's slotted plugin element and resizing only that panel's
|
|
208
216
|
/// `Renderer`. Bound in `BeforeResize` for the DRAGGED panel only (it is
|
|
@@ -347,6 +355,7 @@ impl Component for MainPanel {
|
|
|
347
355
|
main_panel_ref: NodeRef::default(),
|
|
348
356
|
layout_ref: NodeRef::default(),
|
|
349
357
|
inserted: Vec::new(),
|
|
358
|
+
pending_removals: HashSet::new(),
|
|
350
359
|
_layout_update_listener: listener,
|
|
351
360
|
_layout_select_listener: select_listener,
|
|
352
361
|
_layout_before_resize_listener: before_resize_listener,
|
|
@@ -376,6 +385,7 @@ impl Component for MainPanel {
|
|
|
376
385
|
}
|
|
377
386
|
|
|
378
387
|
fn changed(&mut self, ctx: &Context<Self>, old: &Self::Properties) -> bool {
|
|
388
|
+
self.close_unstaged_panels(ctx, old);
|
|
379
389
|
ctx.props() != old
|
|
380
390
|
}
|
|
381
391
|
|
|
@@ -403,3 +413,17 @@ impl Component for MainPanel {
|
|
|
403
413
|
|
|
404
414
|
fn destroy(&mut self, _ctx: &Context<Self>) {}
|
|
405
415
|
}
|
|
416
|
+
|
|
417
|
+
impl MainPanel {
|
|
418
|
+
/// Report closed every panel gone from the model that the layout never
|
|
419
|
+
/// held, since it has no commit to wait for.
|
|
420
|
+
fn close_unstaged_panels(&self, ctx: &Context<Self>, old: &MainPanelProps) {
|
|
421
|
+
for id in &old.panel_ids {
|
|
422
|
+
if !ctx.props().panel_ids.contains(id)
|
|
423
|
+
&& !self.inserted.iter().any(|n| n == id.as_str())
|
|
424
|
+
{
|
|
425
|
+
ctx.props().on_panel_closed.emit(id.as_str().to_owned());
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
@@ -16,34 +16,32 @@
|
|
|
16
16
|
|
|
17
17
|
#[cfg(feature = "llm-agent")]
|
|
18
18
|
pub mod chat_panel;
|
|
19
|
+
pub mod code_editor;
|
|
19
20
|
pub mod column_dropdown;
|
|
20
21
|
pub mod column_selector;
|
|
21
22
|
pub mod column_settings_sidebar;
|
|
22
|
-
pub mod containers;
|
|
23
|
-
pub mod context_menu;
|
|
24
23
|
pub mod copy_dropdown;
|
|
25
24
|
pub mod datetime_column_style;
|
|
25
|
+
pub mod debug_panel;
|
|
26
|
+
pub mod dragdrop_list;
|
|
26
27
|
pub mod editable_header;
|
|
27
28
|
pub mod empty_row;
|
|
28
29
|
pub mod export_dropdown;
|
|
29
30
|
pub mod expression_editor;
|
|
30
31
|
pub mod filter_dropdown;
|
|
31
|
-
pub mod font_loader;
|
|
32
|
-
pub mod form;
|
|
33
32
|
pub mod function_dropdown;
|
|
34
33
|
pub mod global_filter_bar;
|
|
35
34
|
pub mod main_panel;
|
|
36
|
-
pub mod
|
|
35
|
+
pub mod new_panel_menu;
|
|
37
36
|
pub mod number_series_style;
|
|
38
37
|
pub mod panel_menu;
|
|
39
38
|
pub mod panel_tab;
|
|
40
39
|
pub mod plugin_selector;
|
|
41
40
|
pub mod plugin_tab;
|
|
42
|
-
pub mod portal;
|
|
43
41
|
pub mod render_warning;
|
|
42
|
+
pub mod rows_counter;
|
|
44
43
|
pub mod settings_panel;
|
|
45
44
|
pub mod status_bar;
|
|
46
|
-
pub mod status_bar_counter;
|
|
47
45
|
pub mod status_indicator;
|
|
48
46
|
pub mod string_column_style;
|
|
49
47
|
pub mod style;
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
use std::rc::Rc;
|
|
14
|
+
|
|
15
|
+
use yew::prelude::*;
|
|
16
|
+
|
|
17
|
+
/// A pick in the "New" menu: a fresh panel on a hosted `Table`, or a copy of
|
|
18
|
+
/// an existing panel (the same operation as the context menu's "Duplicate").
|
|
19
|
+
#[derive(Clone, PartialEq)]
|
|
20
|
+
pub enum NewPanelPick {
|
|
21
|
+
FromTable { client: String, table: String },
|
|
22
|
+
FromPanel(String),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// `(client name, its hosted table names)` per loaded client — the resolved
|
|
26
|
+
/// output of `queries::fetch_hosted_tables`.
|
|
27
|
+
pub type HostedTables = Rc<Vec<(String, Vec<String>)>>;
|
|
28
|
+
|
|
29
|
+
/// `(panel id, title)` per panel, in layout-insertion order.
|
|
30
|
+
pub type PanelLabels = Rc<Vec<(String, Option<String>)>>;
|
|
31
|
+
|
|
32
|
+
#[derive(Properties, PartialEq)]
|
|
33
|
+
pub struct NewPanelMenuProps {
|
|
34
|
+
/// `None` while the hosted-table fetch is in flight.
|
|
35
|
+
pub tables: Option<HostedTables>,
|
|
36
|
+
pub panels: PanelLabels,
|
|
37
|
+
pub callback: Callback<NewPanelPick>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// The "New" menu body shared by the status bar dropdown and the context
|
|
41
|
+
/// menu's "New" flyout.
|
|
42
|
+
#[function_component]
|
|
43
|
+
pub fn NewPanelMenu(props: &NewPanelMenuProps) -> Html {
|
|
44
|
+
let pick = |value: NewPanelPick| props.callback.reform(move |_: MouseEvent| value.clone());
|
|
45
|
+
|
|
46
|
+
let tables = match &props.tables {
|
|
47
|
+
None => html! { <span class="no-results">{ "Loading..." }</span> },
|
|
48
|
+
Some(clients) if clients.iter().all(|(_, tables)| tables.is_empty()) => {
|
|
49
|
+
html! { <span class="no-results">{ "No tables" }</span> }
|
|
50
|
+
},
|
|
51
|
+
Some(clients) => {
|
|
52
|
+
let multi = clients.len() > 1;
|
|
53
|
+
clients
|
|
54
|
+
.iter()
|
|
55
|
+
.map(|(client, tables)| {
|
|
56
|
+
let rows = tables.iter().map(|table| {
|
|
57
|
+
let onmousedown = pick(NewPanelPick::FromTable {
|
|
58
|
+
client: client.clone(),
|
|
59
|
+
table: table.clone(),
|
|
60
|
+
});
|
|
61
|
+
html! { <span class="dropdown-menu-item" {onmousedown}>{ table }</span> }
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
html! {
|
|
65
|
+
<>
|
|
66
|
+
if multi { <span class="dropdown-group-sublabel">{ client }</span> }
|
|
67
|
+
{ for rows }
|
|
68
|
+
</>
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
.collect::<Html>()
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let panels = if props.panels.is_empty() {
|
|
76
|
+
html! { <span class="no-results">{ "No panels" }</span> }
|
|
77
|
+
} else {
|
|
78
|
+
props
|
|
79
|
+
.panels
|
|
80
|
+
.iter()
|
|
81
|
+
.map(|(id, title)| {
|
|
82
|
+
let onmousedown = pick(NewPanelPick::FromPanel(id.clone()));
|
|
83
|
+
let label = title.clone().unwrap_or_else(|| id.clone());
|
|
84
|
+
html! { <span class="dropdown-menu-item" {onmousedown}>{ label }</span> }
|
|
85
|
+
})
|
|
86
|
+
.collect::<Html>()
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
html! {
|
|
90
|
+
<div class="new-panel-menu">
|
|
91
|
+
<span class="dropdown-group-label" data-label="new-from-table" />
|
|
92
|
+
<div class="dropdown-group-container">{ tables }</div>
|
|
93
|
+
<span class="dropdown-group-label" data-label="new-from-panel" />
|
|
94
|
+
<div class="dropdown-group-container">{ panels }</div>
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -13,9 +13,8 @@
|
|
|
13
13
|
use web_sys::{HtmlInputElement, InputEvent};
|
|
14
14
|
use yew::prelude::*;
|
|
15
15
|
|
|
16
|
-
use super::modal::{ModalLink, SetModalLink};
|
|
17
|
-
use crate::components::form::select_enum_field::SelectEnumField;
|
|
18
16
|
use crate::config::*;
|
|
17
|
+
use crate::ui::{IntlLabel, ModalLink, SelectEnumField, SetModalLink};
|
|
19
18
|
use crate::utils::WeakScope;
|
|
20
19
|
|
|
21
20
|
#[derive(Properties)]
|
|
@@ -113,7 +112,7 @@ impl Component for NumberSeriesStyle {
|
|
|
113
112
|
});
|
|
114
113
|
html! {
|
|
115
114
|
<div class="row">
|
|
116
|
-
<
|
|
115
|
+
<IntlLabel name="stack" />
|
|
117
116
|
<input type="checkbox" id="stack-checkbox" {checked} {oninput} />
|
|
118
117
|
</div>
|
|
119
118
|
}
|