@perspective-dev/viewer 5.3.1 → 5.5.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.d.ts +2 -0
- package/dist/esm/perspective-viewer.inline.js +8 -8
- package/dist/esm/perspective-viewer.inline.js.map +2 -2
- package/dist/esm/perspective-viewer.js +5 -5
- package/dist/esm/perspective-viewer.js.map +2 -2
- package/dist/esm/plugin.d.ts +9 -0
- package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
- package/dist/esm/ts-rs/GetViewMode.d.ts +4 -0
- package/dist/esm/ts-rs/GetViewOptions.d.ts +8 -0
- package/dist/esm/ts-rs/RestoreOptions.d.ts +6 -0
- package/dist/esm/ts-rs/RestoreWorkspaceOptions.d.ts +9 -0
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +30 -22
- package/dist/wasm/perspective-viewer.d.ts +137 -104
- package/dist/wasm/perspective-viewer.js +337 -269
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +20 -30
- 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 +18 -7
- package/src/css/column-settings-panel.css +0 -225
- package/src/css/column-style.css +175 -0
- package/src/css/config-selector.css +19 -11
- package/src/css/filter-dropdown.css +5 -5
- package/src/css/form/debug.css +37 -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 +63 -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 +2 -1
- package/src/css/viewer.css +32 -26
- package/src/rust/agent/tools.rs +80 -7
- 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 +24 -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 +14 -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/alignment_field.rs +90 -0
- package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +16 -18
- 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 +70 -12
- 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} +112 -55
- 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/msg.rs +4 -0
- 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 +40 -2
- package/src/rust/components/mod.rs +5 -8
- 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 +58 -4
- 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 +8 -2
- package/src/rust/components/status_bar.rs +113 -19
- package/src/rust/components/status_indicator.rs +9 -14
- 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 +53 -19
- package/src/rust/components/viewer/render.rs +16 -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 +11 -22
- 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 +312 -33
- package/src/rust/config/datetime_column_style.rs +0 -11
- package/src/rust/config/mod.rs +0 -4
- package/src/rust/config/options.rs +50 -0
- package/src/rust/config/workspace_config.rs +70 -24
- 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 +194 -115
- package/src/rust/js/plugin.rs +3 -87
- package/src/rust/lib.rs +19 -18
- 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/queries/plugin_column_styles.rs +6 -5
- 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 +201 -41
- package/src/rust/renderer/plugin_store.rs +2 -1
- package/src/rust/renderer/props.rs +7 -1
- package/src/rust/renderer.rs +23 -27
- package/src/rust/session/props.rs +8 -3
- package/src/rust/session/view_subscription.rs +19 -6
- package/src/rust/session.rs +189 -150
- package/src/rust/tasks/create_panel.rs +5 -2
- package/src/rust/tasks/dismiss_render_warning.rs +1 -1
- package/src/rust/tasks/eject.rs +1 -0
- package/src/rust/tasks/pipeline.rs +18 -67
- package/src/rust/tasks/presize_panels.rs +24 -1
- package/src/rust/tasks/restore_and_render.rs +0 -17
- package/src/rust/tasks/restore_panel.rs +129 -30
- package/src/rust/tasks/send_column_config.rs +4 -1
- package/src/rust/tasks/send_plugin_config.rs +1 -1
- package/src/rust/tasks/set_edit_mode.rs +1 -1
- package/src/rust/tasks/sync_update_panels.rs +147 -67
- package/src/rust/tasks/table_lifecycle.rs +7 -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/{config/datetime_column_style/color_mode.rs → ui/form/intl_label.rs} +42 -42
- 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 +175 -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/css_vars.rs +13 -0
- package/src/rust/utils/local_fonts.rs +395 -0
- package/src/rust/utils/mod.rs +4 -0
- package/src/rust/workspace.rs +47 -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/status_pending.svg +3 -0
- 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 +2 -8
- package/src/themes/intl/ar.css +198 -0
- package/src/themes/intl/de.css +63 -40
- package/src/themes/intl/es.css +58 -35
- package/src/themes/intl/fr.css +57 -34
- package/src/themes/intl/ja.css +59 -36
- package/src/themes/intl/pt.css +54 -31
- package/src/themes/intl/zh.css +65 -42
- package/src/themes/intl.css +34 -8
- 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/perspective-viewer.ts +2 -0
- package/src/ts/plugin.ts +9 -0
- package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
- package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
- package/src/ts/ts-rs/GetViewOptions.ts +7 -0
- package/src/ts/ts-rs/RestoreOptions.ts +7 -1
- package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
- package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
- package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
- package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
- package/src/css/plugin-settings-panel.css +0 -171
- package/src/rust/components/modal.rs +0 -177
- package/src/rust/components/string_column_style.rs +0 -121
- package/src/rust/config/string_column_style.rs +0 -135
- 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/ts/ts-rs/FormatMode.ts +0 -3
- package/src/ts/ts-rs/StringColorMode.ts +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/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/{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
|
@@ -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())
|
|
@@ -25,6 +25,10 @@ pub enum MainPanelMsg {
|
|
|
25
25
|
/// deliver in every observed context.
|
|
26
26
|
StagedChanged,
|
|
27
27
|
|
|
28
|
+
/// A layout tree was staged for the retained panel set
|
|
29
|
+
/// (`layout_staged` PubSub): re-render so `reconcile` applies it.
|
|
30
|
+
LayoutStaged,
|
|
31
|
+
|
|
28
32
|
/// The `<regular-layout>` tree changed (fired by its
|
|
29
33
|
/// `regular-layout-update` event). Used to detect panels removed from
|
|
30
34
|
/// the layout (e.g. a frame's close button) so they can be disposed.
|
|
@@ -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
|
|
@@ -242,6 +250,9 @@ pub struct MainPanel {
|
|
|
242
250
|
/// `Workspace::staged_changed` → [`MainPanelMsg::StagedChanged`] on THIS
|
|
243
251
|
/// component's scope (see the message doc for why not the root's).
|
|
244
252
|
_staged_sub: crate::utils::Subscription,
|
|
253
|
+
|
|
254
|
+
/// `Workspace::layout_staged` → [`MainPanelMsg::LayoutStaged`].
|
|
255
|
+
_layout_staged_sub: crate::utils::Subscription,
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
impl Component for MainPanel {
|
|
@@ -343,10 +354,20 @@ impl Component for MainPanel {
|
|
|
343
354
|
.add_listener(move |()| cb.emit(()))
|
|
344
355
|
};
|
|
345
356
|
|
|
357
|
+
let layout_staged_sub = {
|
|
358
|
+
use crate::utils::AddListener;
|
|
359
|
+
let cb = ctx.link().callback(|_: ()| MainPanelMsg::LayoutStaged);
|
|
360
|
+
ctx.props()
|
|
361
|
+
.workspace
|
|
362
|
+
.layout_staged()
|
|
363
|
+
.add_listener(move |()| cb.emit(()))
|
|
364
|
+
};
|
|
365
|
+
|
|
346
366
|
Self {
|
|
347
367
|
main_panel_ref: NodeRef::default(),
|
|
348
368
|
layout_ref: NodeRef::default(),
|
|
349
369
|
inserted: Vec::new(),
|
|
370
|
+
pending_removals: HashSet::new(),
|
|
350
371
|
_layout_update_listener: listener,
|
|
351
372
|
_layout_select_listener: select_listener,
|
|
352
373
|
_layout_before_resize_listener: before_resize_listener,
|
|
@@ -359,6 +380,7 @@ impl Component for MainPanel {
|
|
|
359
380
|
theme_backgrounds: HashMap::new(),
|
|
360
381
|
stamped_frame_themes: None,
|
|
361
382
|
_staged_sub: staged_sub,
|
|
383
|
+
_layout_staged_sub: layout_staged_sub,
|
|
362
384
|
}
|
|
363
385
|
}
|
|
364
386
|
|
|
@@ -366,6 +388,7 @@ impl Component for MainPanel {
|
|
|
366
388
|
match msg {
|
|
367
389
|
MainPanelMsg::PointerEvent(event) => self.on_pointer_event(ctx, event),
|
|
368
390
|
MainPanelMsg::StagedChanged => true,
|
|
391
|
+
MainPanelMsg::LayoutStaged => true,
|
|
369
392
|
MainPanelMsg::LayoutUpdated => self.on_layout_updated(ctx),
|
|
370
393
|
MainPanelMsg::TabSelected(name) => self.on_tab_selected(ctx, name),
|
|
371
394
|
MainPanelMsg::ContextMenu(id, x, y) => self.on_context_menu(ctx, id, x, y),
|
|
@@ -376,6 +399,7 @@ impl Component for MainPanel {
|
|
|
376
399
|
}
|
|
377
400
|
|
|
378
401
|
fn changed(&mut self, ctx: &Context<Self>, old: &Self::Properties) -> bool {
|
|
402
|
+
self.close_unstaged_panels(ctx, old);
|
|
379
403
|
ctx.props() != old
|
|
380
404
|
}
|
|
381
405
|
|
|
@@ -403,3 +427,17 @@ impl Component for MainPanel {
|
|
|
403
427
|
|
|
404
428
|
fn destroy(&mut self, _ctx: &Context<Self>) {}
|
|
405
429
|
}
|
|
430
|
+
|
|
431
|
+
impl MainPanel {
|
|
432
|
+
/// Report closed every panel gone from the model that the layout never
|
|
433
|
+
/// held, since it has no commit to wait for.
|
|
434
|
+
fn close_unstaged_panels(&self, ctx: &Context<Self>, old: &MainPanelProps) {
|
|
435
|
+
for id in &old.panel_ids {
|
|
436
|
+
if !ctx.props().panel_ids.contains(id)
|
|
437
|
+
&& !self.inserted.iter().any(|n| n == id.as_str())
|
|
438
|
+
{
|
|
439
|
+
ctx.props().on_panel_closed.emit(id.as_str().to_owned());
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
@@ -16,36 +16,33 @@
|
|
|
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
|
-
pub mod string_column_style;
|
|
49
46
|
pub mod style;
|
|
50
47
|
pub mod style_controls;
|
|
51
48
|
pub mod type_icon;
|
|
@@ -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
|
}
|