@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
|
@@ -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
|
}
|
|
@@ -26,20 +26,23 @@
|
|
|
26
26
|
//! `on_close` fires exactly once, when the session ends (blur dismissal, a
|
|
27
27
|
//! command selection, or the picker closing).
|
|
28
28
|
|
|
29
|
+
use std::rc::Rc;
|
|
30
|
+
|
|
29
31
|
use perspective_js::utils::*;
|
|
30
32
|
use wasm_bindgen::JsCast;
|
|
31
33
|
use web_sys::HtmlElement;
|
|
32
34
|
use yew::prelude::*;
|
|
33
35
|
|
|
34
|
-
use crate::components::context_menu::{ContextMenu, ContextMenuEntry, ContextMenuItem};
|
|
35
36
|
use crate::components::copy_dropdown::CopyDropDownMenu;
|
|
36
37
|
use crate::components::export_dropdown::ExportDropDownMenu;
|
|
37
|
-
use crate::components::
|
|
38
|
+
use crate::components::new_panel_menu::{HostedTables, NewPanelMenu, NewPanelPick};
|
|
38
39
|
use crate::components::style::StyleSurface;
|
|
39
40
|
use crate::config::*;
|
|
40
41
|
use crate::js::copy_to_clipboard;
|
|
41
42
|
use crate::presentation::Presentation;
|
|
43
|
+
use crate::queries::fetch_hosted_tables;
|
|
42
44
|
use crate::tasks::export_method_to_blob;
|
|
45
|
+
use crate::ui::{ContextMenu, ContextMenuEntry, ContextMenuItem, MODAL_SLOT, PortalModal};
|
|
43
46
|
use crate::utils::*;
|
|
44
47
|
use crate::workspace::{PanelId, Workspace};
|
|
45
48
|
|
|
@@ -48,7 +51,13 @@ use crate::workspace::{PanelId, Workspace};
|
|
|
48
51
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
49
52
|
pub enum PanelCommand {
|
|
50
53
|
New,
|
|
51
|
-
NewFrom {
|
|
54
|
+
NewFrom {
|
|
55
|
+
client: String,
|
|
56
|
+
table: String,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/// A fresh panel copied from the named panel.
|
|
60
|
+
NewFromPanel(String),
|
|
52
61
|
Duplicate,
|
|
53
62
|
Reset,
|
|
54
63
|
Maximize,
|
|
@@ -133,8 +142,8 @@ pub enum PanelMenuMsg {
|
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
pub struct PanelMenu {
|
|
136
|
-
/// The session-long 0×0 cursor anchor element (
|
|
137
|
-
/// stages' `PortalModal`s position against; removed on destroy.
|
|
145
|
+
/// The session-long 0×0 cursor anchor element (a viewer light-DOM child)
|
|
146
|
+
/// both stages' `PortalModal`s position against; removed on destroy.
|
|
138
147
|
anchor: HtmlElement,
|
|
139
148
|
|
|
140
149
|
/// The open format picker, if the session is in its picker stage.
|
|
@@ -143,7 +152,7 @@ pub struct PanelMenu {
|
|
|
143
152
|
/// The "New" sub-menu's data: `(client name, its hosted table names)` per
|
|
144
153
|
/// loaded client, in registration order. `None` while the fetch spawned at
|
|
145
154
|
/// menu open is still in flight.
|
|
146
|
-
tables: Option<
|
|
155
|
+
tables: Option<HostedTables>,
|
|
147
156
|
}
|
|
148
157
|
|
|
149
158
|
impl Component for PanelMenu {
|
|
@@ -151,37 +160,20 @@ impl Component for PanelMenu {
|
|
|
151
160
|
type Properties = PanelMenuProps;
|
|
152
161
|
|
|
153
162
|
fn create(ctx: &Context<Self>) -> Self {
|
|
154
|
-
let
|
|
163
|
+
let workspace = ctx.props().workspace.clone();
|
|
155
164
|
let link = ctx.link().clone();
|
|
156
165
|
ApiFuture::spawn(async move {
|
|
157
|
-
let
|
|
158
|
-
for client in clients {
|
|
159
|
-
// A failing client contributes an empty list rather than
|
|
160
|
-
// poisoning the whole menu.
|
|
161
|
-
let mut names = match client.get_hosted_table_names().await {
|
|
162
|
-
Ok(names) => names,
|
|
163
|
-
Err(err) => {
|
|
164
|
-
tracing::warn!(
|
|
165
|
-
"Failed to list tables for `Client` \"{}\": {err}",
|
|
166
|
-
client.get_name()
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
vec![]
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
// Server-side name order isn't meaningful (or stable) — list
|
|
174
|
-
// deterministically.
|
|
175
|
-
names.sort_unstable();
|
|
176
|
-
tables.push((client.get_name().to_owned(), names));
|
|
177
|
-
}
|
|
178
|
-
|
|
166
|
+
let tables = fetch_hosted_tables(&workspace).await;
|
|
179
167
|
link.send_message(PanelMenuMsg::TablesLoaded(tables));
|
|
180
168
|
Ok(())
|
|
181
169
|
});
|
|
182
170
|
|
|
183
171
|
Self {
|
|
184
|
-
anchor: session_anchor(
|
|
172
|
+
anchor: session_anchor(
|
|
173
|
+
ctx.props().presentation.viewer_elem(),
|
|
174
|
+
ctx.props().x,
|
|
175
|
+
ctx.props().y,
|
|
176
|
+
),
|
|
185
177
|
picker: None,
|
|
186
178
|
tables: None,
|
|
187
179
|
}
|
|
@@ -213,7 +205,7 @@ impl Component for PanelMenu {
|
|
|
213
205
|
false
|
|
214
206
|
},
|
|
215
207
|
PanelMenuMsg::TablesLoaded(tables) => {
|
|
216
|
-
self.tables = Some(tables);
|
|
208
|
+
self.tables = Some(Rc::new(tables));
|
|
217
209
|
self.picker.is_none()
|
|
218
210
|
},
|
|
219
211
|
}
|
|
@@ -229,7 +221,7 @@ impl Component for PanelMenu {
|
|
|
229
221
|
fn destroy(&mut self, _ctx: &Context<Self>) {
|
|
230
222
|
// The session ended (or the parent unmounted mid-session, e.g. the
|
|
231
223
|
// target panel closed); don't leak the cursor anchor.
|
|
232
|
-
|
|
224
|
+
self.anchor.remove();
|
|
233
225
|
}
|
|
234
226
|
}
|
|
235
227
|
|
|
@@ -254,7 +246,7 @@ impl PanelMenu {
|
|
|
254
246
|
None => vec![ContextMenuEntry::Submenu {
|
|
255
247
|
label: "New".to_owned(),
|
|
256
248
|
on_select: None,
|
|
257
|
-
entries: self.
|
|
249
|
+
entries: vec![ContextMenuEntry::Custom(self.new_submenu_body(ctx))],
|
|
258
250
|
}],
|
|
259
251
|
Some(panel_id) => {
|
|
260
252
|
let can_close = ctx.props().workspace.len() > 1;
|
|
@@ -263,7 +255,7 @@ impl PanelMenu {
|
|
|
263
255
|
ContextMenuEntry::Submenu {
|
|
264
256
|
label: "New".to_owned(),
|
|
265
257
|
on_select: Some(cmd(PanelCommand::New)),
|
|
266
|
-
entries: self.
|
|
258
|
+
entries: vec![ContextMenuEntry::Custom(self.new_submenu_body(ctx))],
|
|
267
259
|
},
|
|
268
260
|
item("Duplicate", cmd(PanelCommand::Duplicate), false),
|
|
269
261
|
item("Reset", cmd(PanelCommand::Reset), false),
|
|
@@ -301,7 +293,7 @@ impl PanelMenu {
|
|
|
301
293
|
<PortalModal
|
|
302
294
|
key="perspective-context-menu"
|
|
303
295
|
tag_name="perspective-context-menu"
|
|
304
|
-
|
|
296
|
+
sheet={StyleSurface::ContextMenu.sheet()}
|
|
305
297
|
target={Some(self.anchor.clone())}
|
|
306
298
|
own_focus=true
|
|
307
299
|
on_close={&on_close}
|
|
@@ -315,54 +307,35 @@ impl PanelMenu {
|
|
|
315
307
|
}
|
|
316
308
|
}
|
|
317
309
|
|
|
318
|
-
/// The "New" hover sub-menu's
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
for table in tables {
|
|
347
|
-
let on_select = {
|
|
348
|
-
clone!(client_name, table);
|
|
349
|
-
ctx.link().callback(move |_| {
|
|
350
|
-
PanelMenuMsg::Command(PanelCommand::NewFrom {
|
|
351
|
-
client: client_name.clone(),
|
|
352
|
-
table: table.clone(),
|
|
353
|
-
})
|
|
354
|
-
})
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
entries.push(ContextMenuEntry::Item(ContextMenuItem {
|
|
358
|
-
label: table.clone(),
|
|
359
|
-
on_select,
|
|
360
|
-
disabled: false,
|
|
361
|
-
}));
|
|
362
|
-
}
|
|
363
|
-
}
|
|
310
|
+
/// The "New" hover sub-menu's body, the shared [`NewPanelMenu`].
|
|
311
|
+
fn new_submenu_body(&self, ctx: &Context<Self>) -> Html {
|
|
312
|
+
let panels = Rc::new(
|
|
313
|
+
ctx.props()
|
|
314
|
+
.workspace
|
|
315
|
+
.panel_ids()
|
|
316
|
+
.into_iter()
|
|
317
|
+
.filter_map(|id| {
|
|
318
|
+
let panel = ctx.props().workspace.panel(&id)?;
|
|
319
|
+
let title = panel.session.get_title().filter(|t| !t.is_empty());
|
|
320
|
+
Some((id.as_str().to_owned(), title))
|
|
321
|
+
})
|
|
322
|
+
.collect::<Vec<_>>(),
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
let callback = ctx.link().batch_callback(|pick: NewPanelPick| {
|
|
326
|
+
let cmd = match pick {
|
|
327
|
+
NewPanelPick::FromTable { client, table } => {
|
|
328
|
+
PanelMenuMsg::Command(PanelCommand::NewFrom { client, table })
|
|
329
|
+
},
|
|
330
|
+
NewPanelPick::FromPanel(id) => {
|
|
331
|
+
PanelMenuMsg::Command(PanelCommand::NewFromPanel(id))
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
vec![cmd, PanelMenuMsg::MenuClosed]
|
|
336
|
+
});
|
|
364
337
|
|
|
365
|
-
|
|
338
|
+
html! { <NewPanelMenu tables={self.tables.clone()} {panels} {callback} /> }
|
|
366
339
|
}
|
|
367
340
|
|
|
368
341
|
/// Export/Copy format-picker spawned in place of the context menu, anchored
|
|
@@ -462,7 +435,7 @@ impl PanelMenu {
|
|
|
462
435
|
<PortalModal
|
|
463
436
|
key={tag_name}
|
|
464
437
|
{tag_name}
|
|
465
|
-
|
|
438
|
+
sheet={StyleSurface::DropdownMenu.sheet()}
|
|
466
439
|
{target}
|
|
467
440
|
own_focus=true
|
|
468
441
|
{on_close}
|
|
@@ -474,20 +447,21 @@ impl PanelMenu {
|
|
|
474
447
|
}
|
|
475
448
|
}
|
|
476
449
|
|
|
477
|
-
/// Create
|
|
478
|
-
///
|
|
479
|
-
fn session_anchor(x: f64, y: f64) -> HtmlElement {
|
|
450
|
+
/// Create the session-long 0×0 cursor anchor at viewport `(x, y)` as a
|
|
451
|
+
/// light-DOM child of `viewer` in the modal slot.
|
|
452
|
+
fn session_anchor(viewer: &HtmlElement, x: f64, y: f64) -> HtmlElement {
|
|
480
453
|
let anchor: HtmlElement = global::document()
|
|
481
454
|
.create_element("div")
|
|
482
455
|
.unwrap()
|
|
483
456
|
.unchecked_into();
|
|
484
457
|
|
|
458
|
+
let _ = anchor.set_attribute("slot", MODAL_SLOT);
|
|
485
459
|
let style = anchor.style();
|
|
486
460
|
let _ = style.set_property("position", "fixed");
|
|
487
461
|
let _ = style.set_property("left", &format!("{x}px"));
|
|
488
462
|
let _ = style.set_property("top", &format!("{y}px"));
|
|
489
463
|
let _ = style.set_property("width", "0px");
|
|
490
464
|
let _ = style.set_property("height", "0px");
|
|
491
|
-
let _ =
|
|
465
|
+
let _ = viewer.append_child(&anchor);
|
|
492
466
|
anchor
|
|
493
467
|
}
|