@perspective-dev/viewer 5.3.0 → 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 +8 -8
- package/dist/cdn/perspective-viewer.js.map +3 -3
- package/dist/css/blueprint.css +1 -0
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.css +1 -0
- 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 -0
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.css +1 -0
- 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 -0
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/column-format.d.ts +16 -3
- package/dist/esm/extensions.d.ts +0 -5
- package/dist/esm/perspective-viewer.inline.js +7 -7
- package/dist/esm/perspective-viewer.inline.js.map +3 -3
- package/dist/esm/perspective-viewer.js +8 -8
- package/dist/esm/perspective-viewer.js.map +3 -3
- 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 +230 -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 -206
- package/src/css/column-style.css +95 -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 +6 -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/{containers → ui/containers}/tabs.css +13 -3
- package/src/css/{dom → ui/dom}/select.css +19 -8
- package/src/css/viewer.css +32 -31
- package/src/rust/agent/tools.rs +5 -2
- 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 +296 -238
- 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 +16 -6
- package/src/rust/components/datetime_column_style.rs +51 -12
- 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 +125 -87
- 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 +3 -2
- package/src/rust/components/status_bar.rs +113 -20
- 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 +25 -15
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +7 -3
- package/src/rust/components/style_controls/number_string_format/style_section.rs +2 -1
- package/src/rust/components/style_controls/number_string_format/types.rs +24 -0
- package/src/rust/components/style_controls/number_string_format.rs +37 -14
- 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 +358 -52
- package/src/rust/config/datetime_column_style.rs +4 -21
- package/src/rust/config/mod.rs +0 -2
- package/src/rust/config/number_string_format.rs +211 -18
- 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 +85 -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/queries/plugin_column_styles.rs +2 -2
- package/src/rust/renderer/activate.rs +1 -3
- package/src/rust/renderer/dispatch.rs +117 -30
- package/src/rust/renderer/limits.rs +24 -5
- package/src/rust/renderer/plugin_config.rs +56 -8
- 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/ui/containers/control_group.rs +113 -0
- package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
- package/src/rust/{components → ui}/containers/mod.rs +1 -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 +8 -8
- 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 +133 -0
- package/src/themes/botanical.css +3 -52
- package/src/themes/defaults.css +3 -144
- package/src/themes/dracula.css +1 -71
- package/src/themes/eggplant.css +131 -0
- package/src/themes/gruvbox-dark.css +2 -79
- package/src/themes/gruvbox.css +2 -40
- package/src/themes/icons.css +2 -10
- package/src/themes/intl/ar.css +185 -0
- package/src/themes/intl/de.css +65 -33
- package/src/themes/intl/es.css +60 -28
- package/src/themes/intl/fr.css +60 -28
- package/src/themes/intl/ja.css +62 -30
- package/src/themes/intl/pt.css +58 -26
- package/src/themes/intl/zh.css +68 -36
- package/src/themes/intl.css +42 -7
- package/src/themes/ledger.css +137 -0
- package/src/themes/monokai.css +1 -64
- package/src/themes/nord.css +131 -0
- package/src/themes/phosphor.css +1 -61
- package/src/themes/pro-dark.css +1 -52
- package/src/themes/pro.css +98 -0
- package/src/themes/solarized-dark.css +1 -46
- package/src/themes/solarized.css +1 -24
- package/src/themes/themes.css +10 -5
- package/src/themes/vaporwave.css +1 -57
- package/src/themes/velvet.css +132 -0
- package/src/ts/column-format.ts +52 -2
- package/src/ts/extensions.ts +0 -13
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
- package/src/css/plugin-settings-panel.css +0 -99
- 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/src/svg/dropdown-selector-light.svg +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → 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/{dom → ui/dom}/checkbox.css +0 -0
- /package/src/css/{dom → ui/dom}/scrollbar.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
|
@@ -22,11 +22,10 @@ use perspective_js::utils::global::navigator;
|
|
|
22
22
|
use wasm_bindgen::prelude::*;
|
|
23
23
|
use yew::prelude::*;
|
|
24
24
|
|
|
25
|
-
use super::modal::{ModalLink, SetModalLink};
|
|
26
25
|
use crate::components::datetime_column_style::custom::DatetimeStyleCustom;
|
|
27
26
|
use crate::components::datetime_column_style::simple::DatetimeStyleSimple;
|
|
28
|
-
use crate::components::form::select_value_field::SelectValueField;
|
|
29
27
|
use crate::config::*;
|
|
28
|
+
use crate::ui::{ModalLink, SelectValueField, SetModalLink};
|
|
30
29
|
use crate::utils::WeakScope;
|
|
31
30
|
|
|
32
31
|
/// Format-only widget for `datetime` columns. Renders the `date_format`
|
|
@@ -38,6 +37,9 @@ pub struct DatetimeColumnStyleProps {
|
|
|
38
37
|
pub enable_time_config: bool,
|
|
39
38
|
pub config: Option<DatetimeColumnStyleConfig>,
|
|
40
39
|
|
|
40
|
+
/// The active plugin's declared default `date_format`.
|
|
41
|
+
pub default_format: Rc<DatetimeFormatType>,
|
|
42
|
+
|
|
41
43
|
#[prop_or_default]
|
|
42
44
|
pub on_change: Callback<ColumnConfigFieldUpdate>,
|
|
43
45
|
|
|
@@ -57,7 +59,9 @@ impl ModalLink<DatetimeColumnStyle> for DatetimeColumnStyleProps {
|
|
|
57
59
|
|
|
58
60
|
impl PartialEq for DatetimeColumnStyleProps {
|
|
59
61
|
fn eq(&self, other: &Self) -> bool {
|
|
60
|
-
self.enable_time_config == other.enable_time_config
|
|
62
|
+
self.enable_time_config == other.enable_time_config
|
|
63
|
+
&& self.config == other.config
|
|
64
|
+
&& self.default_format == other.default_format
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
|
|
@@ -90,7 +94,9 @@ impl Component for DatetimeColumnStyle {
|
|
|
90
94
|
std::mem::swap(&mut self.config, &mut new_config);
|
|
91
95
|
rerender = true;
|
|
92
96
|
}
|
|
93
|
-
if old.enable_time_config != ctx.props().enable_time_config
|
|
97
|
+
if old.enable_time_config != ctx.props().enable_time_config
|
|
98
|
+
|| old.default_format != ctx.props().default_format
|
|
99
|
+
{
|
|
94
100
|
rerender = true;
|
|
95
101
|
}
|
|
96
102
|
rerender
|
|
@@ -99,22 +105,24 @@ impl Component for DatetimeColumnStyle {
|
|
|
99
105
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
|
100
106
|
match msg {
|
|
101
107
|
DatetimeColumnStyleMsg::TimezoneChanged(val) => {
|
|
108
|
+
let mut format = self.effective_format(ctx);
|
|
102
109
|
if Some(&*USER_TIMEZONE) != val.as_ref() {
|
|
103
|
-
*
|
|
110
|
+
*format.time_zone_mut() = val;
|
|
104
111
|
} else {
|
|
105
|
-
*
|
|
112
|
+
*format.time_zone_mut() = None;
|
|
106
113
|
}
|
|
107
114
|
|
|
115
|
+
self.set_format(ctx, format);
|
|
108
116
|
self.dispatch_config(ctx);
|
|
109
117
|
true
|
|
110
118
|
},
|
|
111
119
|
DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged(simple) => {
|
|
112
|
-
self.
|
|
120
|
+
self.set_format(ctx, DatetimeFormatType::Simple(simple));
|
|
113
121
|
self.dispatch_config(ctx);
|
|
114
122
|
true
|
|
115
123
|
},
|
|
116
124
|
DatetimeColumnStyleMsg::CustomDatetimeStyleConfigChanged(custom) => {
|
|
117
|
-
self.
|
|
125
|
+
self.set_format(ctx, DatetimeFormatType::Custom(custom));
|
|
118
126
|
self.dispatch_config(ctx);
|
|
119
127
|
true
|
|
120
128
|
},
|
|
@@ -122,6 +130,24 @@ impl Component for DatetimeColumnStyle {
|
|
|
122
130
|
}
|
|
123
131
|
|
|
124
132
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
|
133
|
+
let format = self.effective_format(ctx);
|
|
134
|
+
let (date_style_default, time_style_default) = match &*ctx.props().default_format {
|
|
135
|
+
DatetimeFormatType::Simple(simple) => (simple.date_style, simple.time_style),
|
|
136
|
+
DatetimeFormatType::Custom(_) => {
|
|
137
|
+
(SimpleDatetimeFormat::Short, SimpleDatetimeFormat::Medium)
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
let simple_reset = {
|
|
142
|
+
let default_format = ctx.props().default_format.clone();
|
|
143
|
+
ctx.link().callback(move |_| {
|
|
144
|
+
DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged(match &*default_format {
|
|
145
|
+
DatetimeFormatType::Simple(simple) => simple.clone(),
|
|
146
|
+
DatetimeFormatType::Custom(_) => SimpleDatetimeStyleConfig::default(),
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
};
|
|
150
|
+
|
|
125
151
|
html! {
|
|
126
152
|
<>
|
|
127
153
|
<div id="column-style-container" class="datetime-column-style-container">
|
|
@@ -131,10 +157,10 @@ impl Component for DatetimeColumnStyle {
|
|
|
131
157
|
values={ALL_TIMEZONES.with(|x| (*x).clone())}
|
|
132
158
|
default_value={(*USER_TIMEZONE).clone()}
|
|
133
159
|
on_change={ctx.link().callback(DatetimeColumnStyleMsg::TimezoneChanged)}
|
|
134
|
-
current_value={
|
|
160
|
+
current_value={format.time_zone().as_ref().unwrap_or(&*USER_TIMEZONE).clone()}
|
|
135
161
|
/>
|
|
136
162
|
}
|
|
137
|
-
if let DatetimeFormatType::Simple(config) = &
|
|
163
|
+
if let DatetimeFormatType::Simple(config) = &format {
|
|
138
164
|
if ctx.props().enable_time_config {
|
|
139
165
|
<div class="row">
|
|
140
166
|
<button
|
|
@@ -149,15 +175,17 @@ impl Component for DatetimeColumnStyle {
|
|
|
149
175
|
enable_time_config={ctx.props().enable_time_config}
|
|
150
176
|
on_change={ctx.link().callback(DatetimeColumnStyleMsg::SimpleDatetimeStyleConfigChanged)}
|
|
151
177
|
config={config.clone()}
|
|
178
|
+
{date_style_default}
|
|
179
|
+
{time_style_default}
|
|
152
180
|
/>
|
|
153
|
-
} else if let DatetimeFormatType::Custom(config) = &
|
|
181
|
+
} else if let DatetimeFormatType::Custom(config) = &format {
|
|
154
182
|
if ctx.props().enable_time_config {
|
|
155
183
|
<div class="row">
|
|
156
184
|
<button
|
|
157
185
|
id="datetime_format"
|
|
158
186
|
data-title="Custom"
|
|
159
187
|
data-title-hover="Switch to Simple"
|
|
160
|
-
onclick={
|
|
188
|
+
onclick={simple_reset}
|
|
161
189
|
/>
|
|
162
190
|
</div>
|
|
163
191
|
}
|
|
@@ -201,6 +229,17 @@ static USER_TIMEZONE: LazyLock<String> = LazyLock::new(|| {
|
|
|
201
229
|
});
|
|
202
230
|
|
|
203
231
|
impl DatetimeColumnStyle {
|
|
232
|
+
fn effective_format(&self, ctx: &Context<Self>) -> DatetimeFormatType {
|
|
233
|
+
self.config
|
|
234
|
+
.date_format
|
|
235
|
+
.clone()
|
|
236
|
+
.unwrap_or_else(|| (*ctx.props().default_format).clone())
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
fn set_format(&mut self, ctx: &Context<Self>, format: DatetimeFormatType) {
|
|
240
|
+
self.config.date_format = (format != *ctx.props().default_format).then_some(format);
|
|
241
|
+
}
|
|
242
|
+
|
|
204
243
|
/// When this config has changed, we must signal the wrapper element.
|
|
205
244
|
fn dispatch_config(&self, ctx: &Context<Self>) {
|
|
206
245
|
let value = if self.config == DatetimeColumnStyleConfig::default() {
|
|
@@ -17,7 +17,7 @@ use perspective_js::utils::{ApiFuture, JsValueSerdeExt};
|
|
|
17
17
|
use wasm_bindgen::prelude::*;
|
|
18
18
|
use yew::prelude::*;
|
|
19
19
|
|
|
20
|
-
use crate::components::
|
|
20
|
+
use crate::components::code_editor::CodeEditor;
|
|
21
21
|
use crate::js::{MimeType, copy_to_clipboard, paste_from_clipboard};
|
|
22
22
|
use crate::presentation::*;
|
|
23
23
|
use crate::renderer::*;
|
|
@@ -169,10 +169,10 @@ pub fn debug_panel(props: &DebugPanelProps) -> Html {
|
|
|
169
169
|
<div id="debug-panel-overflow">
|
|
170
170
|
<div id="debug-panel" class="sidebar_column" ref={sizer}>
|
|
171
171
|
<div id="debug-panel-controls">
|
|
172
|
-
<button disabled={!*modified} onclick={onapply}
|
|
173
|
-
<button disabled={!*modified} onclick={onreset}
|
|
174
|
-
<button onclick={oncopy}
|
|
175
|
-
<button onclick={onpaste}
|
|
172
|
+
<button id="debug-panel-apply" disabled={!*modified} onclick={onapply} />
|
|
173
|
+
<button id="debug-panel-reset" disabled={!*modified} onclick={onreset} />
|
|
174
|
+
<button id="debug-panel-copy" onclick={oncopy} />
|
|
175
|
+
<button id="debug-panel-paste" onclick={onpaste} />
|
|
176
176
|
</div>
|
|
177
177
|
<div id="debug-panel-editor">
|
|
178
178
|
<CodeEditor
|
|
@@ -23,6 +23,7 @@ use crate::components::column_dropdown::ColumnDropDownElement;
|
|
|
23
23
|
use crate::components::column_selector::{EmptyColumn, InPlaceColumn, InvalidColumn};
|
|
24
24
|
use crate::components::type_icon::TypeIcon;
|
|
25
25
|
use crate::presentation::{DragDropContainer, Presentation};
|
|
26
|
+
use crate::ui::intl_slug;
|
|
26
27
|
use crate::utils::DragTarget;
|
|
27
28
|
|
|
28
29
|
#[derive(Properties, Derivative)]
|
|
@@ -405,16 +406,7 @@ where
|
|
|
405
406
|
// here while the var VALUE stays in the stylesheets - custom
|
|
406
407
|
// properties inherit, so the label's `:before` picks it up.
|
|
407
408
|
let role_style = ctx.props().role_label.as_ref().map(|role| {
|
|
408
|
-
let slug = role
|
|
409
|
-
.chars()
|
|
410
|
-
.map(|x| {
|
|
411
|
-
if x.is_ascii_alphanumeric() {
|
|
412
|
-
x.to_ascii_lowercase()
|
|
413
|
-
} else {
|
|
414
|
-
'-'
|
|
415
|
-
}
|
|
416
|
-
})
|
|
417
|
-
.collect::<String>();
|
|
409
|
+
let slug = intl_slug(role);
|
|
418
410
|
|
|
419
411
|
// The second var is a PRESENCE FLAG: CSS cannot ask whether a
|
|
420
412
|
// custom property is set, so the slot's stylesheet reads it as
|
|
@@ -14,10 +14,10 @@ use std::rc::Rc;
|
|
|
14
14
|
|
|
15
15
|
use yew::prelude::*;
|
|
16
16
|
|
|
17
|
-
use super::containers::dropdown_menu::*;
|
|
18
17
|
use crate::config::*;
|
|
19
18
|
use crate::renderer::*;
|
|
20
19
|
use crate::session::Session;
|
|
20
|
+
use crate::ui::{DropDownMenu, DropDownMenuItem};
|
|
21
21
|
|
|
22
22
|
pub type ExportDropDownMenuItem = DropDownMenuItem<ExportFile>;
|
|
23
23
|
|
|
@@ -15,7 +15,7 @@ use std::rc::Rc;
|
|
|
15
15
|
use perspective_client::{ExprValidationError, clone};
|
|
16
16
|
use yew::prelude::*;
|
|
17
17
|
|
|
18
|
-
use super::
|
|
18
|
+
use super::code_editor::CodeEditor;
|
|
19
19
|
use crate::session::{Session, SessionMetadataRc};
|
|
20
20
|
use crate::tasks::{ExprValidation, validate_expression};
|
|
21
21
|
|
|
@@ -19,9 +19,9 @@ use web_sys::*;
|
|
|
19
19
|
use yew::html::ImplicitClone;
|
|
20
20
|
use yew::prelude::*;
|
|
21
21
|
|
|
22
|
-
use super::portal::PortalModal;
|
|
23
22
|
use super::style::StyleSurface;
|
|
24
23
|
use crate::session::Session;
|
|
24
|
+
use crate::ui::PortalModal;
|
|
25
25
|
use crate::utils::*;
|
|
26
26
|
use crate::*;
|
|
27
27
|
|
|
@@ -219,7 +219,7 @@ impl Component for FilterDropDownPortal {
|
|
|
219
219
|
html! {
|
|
220
220
|
<PortalModal
|
|
221
221
|
tag_name="perspective-dropdown"
|
|
222
|
-
|
|
222
|
+
sheet={StyleSurface::FilterDropdown.sheet()}
|
|
223
223
|
{target}
|
|
224
224
|
own_focus=false
|
|
225
225
|
{on_close}
|
|
@@ -19,8 +19,8 @@ use web_sys::*;
|
|
|
19
19
|
use yew::html::ImplicitClone;
|
|
20
20
|
use yew::prelude::*;
|
|
21
21
|
|
|
22
|
-
use super::portal::PortalModal;
|
|
23
22
|
use super::style::StyleSurface;
|
|
23
|
+
use crate::ui::PortalModal;
|
|
24
24
|
use crate::utils::*;
|
|
25
25
|
|
|
26
26
|
#[derive(Default)]
|
|
@@ -157,7 +157,7 @@ impl Component for FunctionDropDownPortal {
|
|
|
157
157
|
html! {
|
|
158
158
|
<PortalModal
|
|
159
159
|
tag_name="perspective-dropdown"
|
|
160
|
-
|
|
160
|
+
sheet={StyleSurface::FunctionDropdown.sheet()}
|
|
161
161
|
{target}
|
|
162
162
|
own_focus=false
|
|
163
163
|
{on_close}
|
|
@@ -21,42 +21,6 @@
|
|
|
21
21
|
//! own per-panel themed background), but while the plugin is missing — not yet
|
|
22
22
|
//! mounted, first draw pending, or torn down — the host-theme container shows
|
|
23
23
|
//! through a panel themed differently, as a visible artifact.
|
|
24
|
-
//!
|
|
25
|
-
//! There is no native cascade channel from the light-DOM plugin to the frame
|
|
26
|
-
//! (siblings in the flattened tree), so the value is mirrored imperatively:
|
|
27
|
-
//! each panel's *plugin element* — light-DOM, slotted under the panel's slot,
|
|
28
|
-
//! mounted eagerly at panel creation (`create_panel_model` /
|
|
29
|
-
//! `Renderer::mount_active_plugin`) — is where the document theme rules
|
|
30
|
-
//! actually resolve, so its computed `--psp--background-color` is copied onto
|
|
31
|
-
//! the frame's inline style, where the frame's shadow parts inherit it. The
|
|
32
|
-
//! plugin is the ONLY sound source element:
|
|
33
|
-
//!
|
|
34
|
-
//! - An owned hidden probe child of the viewer does NOT work: an *unslotted*
|
|
35
|
-
//! light-DOM child of a shadow host is outside the flattened tree, and
|
|
36
|
-
//! `getComputedStyle` returns all-empty for it even though the document rules
|
|
37
|
-
//! match it syntactically (selector matching is DOM-tree; style computation
|
|
38
|
-
//! is flat-tree).
|
|
39
|
-
//! - The `<perspective-viewer-tab>` is slotted, but stamps its own `theme` attr
|
|
40
|
-
//! in `PanelTab::rendered` — an unordered sibling lifecycle relative to this
|
|
41
|
-
//! pass (the theme-stamp-lag class of bug).
|
|
42
|
-
//! - The plugin's `theme` attr, by contrast, is stamped *synchronously at the
|
|
43
|
-
//! mutation sites* (`update_theme`, `restorePanel`, …) before the events that
|
|
44
|
-
//! schedule this render, so it is current when this pass reads it.
|
|
45
|
-
//!
|
|
46
|
-
//! A read is trusted ONLY when the plugin's stamped attr equals the theme
|
|
47
|
-
//! being mirrored: a freshly-created panel's plugin is mounted *unstamped*
|
|
48
|
-
//! (the attr first lands inside its first locked dispatch), and reading it
|
|
49
|
-
//! early would silently mirror the HOST theme's values. Trusted reads seed
|
|
50
|
-
//! [`MainPanel::theme_backgrounds`], the fallback for frames with no readable
|
|
51
|
-
//! plugin (an unregistered plugin name stays lazily unmounted; plugin-switch
|
|
52
|
-
//! and teardown transients). A frame with neither leaves the pass gate
|
|
53
|
-
//! unlatched, so it retries on subsequent renders until the dispatch stamp
|
|
54
|
-
//! lands (the render that follows that dispatch's `update_count` bump).
|
|
55
|
-
//!
|
|
56
|
-
//! Unlike the plugin `theme`/`active` stamps (see the NOTE in [`reconcile`]),
|
|
57
|
-
//! the frame is pure viewer chrome — no plugin dispatch reads it — so
|
|
58
|
-
//! stamping it from this async `rendered` pass cannot split a plugin draw
|
|
59
|
-
//! across paints, and it needs no lock.
|
|
60
24
|
|
|
61
25
|
use wasm_bindgen::prelude::*;
|
|
62
26
|
use yew::prelude::*;
|
|
@@ -69,12 +33,7 @@ use crate::workspace::PanelId;
|
|
|
69
33
|
/// `.rl-panel::part(container)` background resolves.
|
|
70
34
|
const BACKGROUND_VAR: &str = "--psp--background-color";
|
|
71
35
|
|
|
72
|
-
/// The inputs the frame backgrounds were last computed from
|
|
73
|
-
/// skipped while these are unchanged, so the forced style recalcs of
|
|
74
|
-
/// `getComputedStyle` don't run on every render (e.g. the per-update
|
|
75
|
-
/// `update_count` renders of a streaming table). `available_themes` is part of
|
|
76
|
-
/// the key because theme CSS registering late changes the *computed* value
|
|
77
|
-
/// under an unchanged theme name.
|
|
36
|
+
/// The inputs the frame backgrounds were last computed from.
|
|
78
37
|
pub(super) type FrameThemeSnapshot = (
|
|
79
38
|
Vec<PanelId>,
|
|
80
39
|
Vec<(String, Option<String>)>,
|
|
@@ -82,12 +41,7 @@ pub(super) type FrameThemeSnapshot = (
|
|
|
82
41
|
);
|
|
83
42
|
|
|
84
43
|
impl MainPanel {
|
|
85
|
-
/// Mirror each panel's effective theme background
|
|
86
|
-
/// registry default — the same fallback the tab/menu use) onto its
|
|
87
|
-
/// `<regular-layout-frame>`'s inline `--psp--background-color`, read off
|
|
88
|
-
/// the panel's stamped plugin element (else the theme-keyed cache of prior
|
|
89
|
-
/// reads). A panel with no resolvable theme has the property removed,
|
|
90
|
-
/// falling back to the inherited host-theme value.
|
|
44
|
+
/// Mirror each panel's effective theme background.
|
|
91
45
|
pub(super) fn stamp_frame_themes(&mut self, ctx: &Context<Self>) {
|
|
92
46
|
let Some(layout) = self.layout_ref.cast::<web_sys::Element>() else {
|
|
93
47
|
return;
|
|
@@ -104,8 +58,6 @@ impl MainPanel {
|
|
|
104
58
|
return;
|
|
105
59
|
}
|
|
106
60
|
|
|
107
|
-
// A theme-registry change can re-value an unchanged theme name, so
|
|
108
|
-
// cached reads from the previous registry are unsound.
|
|
109
61
|
if let Some((_, _, prev_themes)) = &self.stamped_frame_themes
|
|
110
62
|
&& prev_themes != &snapshot.2
|
|
111
63
|
{
|
|
@@ -155,19 +107,11 @@ impl MainPanel {
|
|
|
155
107
|
}
|
|
156
108
|
}
|
|
157
109
|
|
|
158
|
-
// Latch only a fully-resolved pass; an unresolved frame (unmounted or
|
|
159
|
-
// not-yet-stamped plugin) retries each render until its dispatch
|
|
160
|
-
// stamp lands.
|
|
161
110
|
self.stamped_frame_themes = complete.then_some(snapshot);
|
|
162
111
|
}
|
|
163
112
|
}
|
|
164
113
|
|
|
165
|
-
/// Read the computed [`BACKGROUND_VAR`] off `slot`'s plugin element
|
|
166
|
-
/// viewer light-DOM child mounted under exactly that slot name (the tab and
|
|
167
|
-
/// toolbar use `tab-`/`statusbar-extra-` prefixed slots). Requires the
|
|
168
|
-
/// plugin's `theme` attr to already equal `theme` — during a plugin switch
|
|
169
|
-
/// two elements briefly share the slot, and a freshly-mounted plugin is not
|
|
170
|
-
/// yet stamped; both are disambiguated by the attr check.
|
|
114
|
+
/// Read the computed [`BACKGROUND_VAR`] off `slot`'s plugin element.
|
|
171
115
|
fn read_plugin_background(viewer: &web_sys::Element, slot: &str, theme: &str) -> Option<String> {
|
|
172
116
|
let children = viewer.children();
|
|
173
117
|
let plugin = (0..children.length())
|
|
@@ -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
|