@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
package/src/rust/presentation.rs
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
13
|
mod column_locator;
|
|
14
|
-
pub mod drag_helpers;
|
|
15
14
|
mod props;
|
|
16
15
|
mod sheets;
|
|
17
16
|
|
|
@@ -28,13 +27,14 @@ use yew::html::ImplicitClone;
|
|
|
28
27
|
use yew::prelude::*;
|
|
29
28
|
|
|
30
29
|
pub use self::column_locator::{
|
|
31
|
-
ColumnLocator, ColumnSettingsTab, ColumnSettingsTarget,
|
|
30
|
+
ColumnLocator, ColumnSettingsTab, ColumnSettingsTarget, OpenColumnSettings,
|
|
32
31
|
};
|
|
33
|
-
use self::drag_helpers::DragTargetState;
|
|
34
|
-
pub use self::drag_helpers::{DragDropContainer, DragEndCallback};
|
|
35
32
|
pub use self::props::{DragDropProps, PresentationProps};
|
|
36
|
-
use crate::
|
|
37
|
-
use crate::
|
|
33
|
+
pub use crate::ui::{DragDropContainer, DragEndCallback};
|
|
34
|
+
use crate::ui::{
|
|
35
|
+
DragTargetState, PointerDownCallback, clear_document_selection, closest_draggable,
|
|
36
|
+
};
|
|
37
|
+
use crate::utils::{CssKind, NamedValue, assign_palette_names, *};
|
|
38
38
|
|
|
39
39
|
#[derive(Clone, Debug)]
|
|
40
40
|
struct DragFrom {
|
|
@@ -73,14 +73,7 @@ pub struct PresentationHandle {
|
|
|
73
73
|
pub agent: crate::agent::AgentSlot,
|
|
74
74
|
|
|
75
75
|
/// The available themes as detected in the browser environment or set
|
|
76
|
-
/// explicitly when CORS prevents detection
|
|
77
|
-
/// external, not component state. `None` until first parsed (detection
|
|
78
|
-
/// is expensive and must wait for `document.styleSheets`). Sync-readable
|
|
79
|
-
/// (a `RefCell`, never borrowed across an await) so synchronous paths
|
|
80
|
-
/// ("stamp with commit" — panel creation, theme mutation sites) can
|
|
81
|
-
/// derive the registry default without awaiting registry init; derived
|
|
82
|
-
/// values (e.g. the default theme = first registered) are computed from
|
|
83
|
-
/// it on demand, never mirrored.
|
|
76
|
+
/// explicitly when CORS prevents detection.
|
|
84
77
|
themes: RefCell<Option<Vec<String>>>,
|
|
85
78
|
|
|
86
79
|
/// Single-flight guard for the stylesheet parse that populates
|
|
@@ -91,12 +84,6 @@ pub struct PresentationHandle {
|
|
|
91
84
|
|
|
92
85
|
/// Whether the host's theme was ever EXPLICITLY chosen — authored as a
|
|
93
86
|
/// `theme` attribute, or set by name through [`Self::set_theme_name`].
|
|
94
|
-
///
|
|
95
|
-
/// `false` means the host merely displays the registry default, which
|
|
96
|
-
/// [`Self::reset_themes`] is free to move; `true` pins the selection
|
|
97
|
-
/// until it leaves the registry. The `theme` attribute alone cannot
|
|
98
|
-
/// carry this, because `init` stamps it unconditionally so the document
|
|
99
|
-
/// cascade has a theme to match.
|
|
100
87
|
theme_selected: Cell<bool>,
|
|
101
88
|
|
|
102
89
|
palette: RefCell<BTreeMap<String, String>>,
|
|
@@ -104,6 +91,8 @@ pub struct PresentationHandle {
|
|
|
104
91
|
open_column_settings: RefCell<OpenColumnSettings>,
|
|
105
92
|
is_workspace: RefCell<Option<bool>>,
|
|
106
93
|
|
|
94
|
+
collapsed_control_groups: RefCell<HashSet<String>>,
|
|
95
|
+
|
|
107
96
|
/// Drag/drop in-progress state. Empty (`NoDrag`) when no user drag is
|
|
108
97
|
/// active. Mutated by `notify_drag_*` / `notify_drop`; read by component
|
|
109
98
|
/// CSS-class derivations (`is_dragover`, `get_drag_column`).
|
|
@@ -124,32 +113,22 @@ pub struct PresentationHandle {
|
|
|
124
113
|
/// dragged element from the shadow tree.
|
|
125
114
|
host_dragend: RefCell<Option<DragEndCallback>>,
|
|
126
115
|
|
|
116
|
+
/// Host-level `pointerdown` listener that clears a stale page selection
|
|
117
|
+
/// before it can turn a row drag into a browser selection drag.
|
|
118
|
+
host_pointerdown: RefCell<Option<PointerDownCallback>>,
|
|
119
|
+
|
|
127
120
|
source_dragend: RefCell<Option<(web_sys::EventTarget, DragEndCallback)>>,
|
|
128
121
|
|
|
129
122
|
/// IntersectionObserver-based fallback for the drag image, kept alive for
|
|
130
123
|
/// the duration of the drag.
|
|
131
124
|
drag_target: RefCell<Option<DragTargetState>>,
|
|
132
125
|
|
|
133
|
-
/// `(open, announce)` — `announce` says this toggle is the SOLE carrier
|
|
134
|
-
/// of the config change (a user gesture: toolbar, `toggleConfig`), so
|
|
135
|
-
/// the element must emit `toggle-settings` + a config-update for it. An
|
|
136
|
-
/// API restore's toggle is announced by its own view-config commit
|
|
137
|
-
/// dispatch instead — announcing here too would double-emit, the first
|
|
138
|
-
/// carrying the intermediate config (settings flipped, view fields not
|
|
139
|
-
/// yet committed).
|
|
140
126
|
pub settings_open_changed: PubSub<(bool, bool)>,
|
|
141
|
-
|
|
142
|
-
/// Injected callback from the root component, replacing the former
|
|
143
|
-
/// `is_workspace_changed: PubSub` field.
|
|
144
127
|
pub on_is_workspace_changed: RefCell<Option<Callback<bool>>>,
|
|
145
128
|
pub settings_before_open_changed: PubSub<bool>,
|
|
146
129
|
pub column_settings_open_changed: PubSub<(bool, Option<String>)>,
|
|
147
130
|
pub theme_config_updated: PubSub<(PtrEqRc<Vec<String>>, Option<usize>)>,
|
|
148
131
|
pub on_eject: PubSub<()>,
|
|
149
|
-
|
|
150
|
-
/// Fires for status-bar / main-panel pointer events that target the
|
|
151
|
-
/// statusbar element. `wire_element_events` formats the `PointerEvent`'s
|
|
152
|
-
/// `type_()` into a `perspective-statusbar-{type}` `CustomEvent` name.
|
|
153
132
|
pub statusbar_pointer_event: PubSub<PointerEvent>,
|
|
154
133
|
}
|
|
155
134
|
|
|
@@ -191,6 +170,7 @@ impl Presentation {
|
|
|
191
170
|
on_is_workspace_changed: Default::default(),
|
|
192
171
|
is_settings_open: Default::default(),
|
|
193
172
|
open_column_settings: Default::default(),
|
|
173
|
+
collapsed_control_groups: Default::default(),
|
|
194
174
|
theme_config_updated: PubSub::default(),
|
|
195
175
|
on_eject: PubSub::default(),
|
|
196
176
|
statusbar_pointer_event: PubSub::default(),
|
|
@@ -199,10 +179,12 @@ impl Presentation {
|
|
|
199
179
|
on_dragstart: Default::default(),
|
|
200
180
|
on_dragend: Default::default(),
|
|
201
181
|
host_dragend: Default::default(),
|
|
182
|
+
host_pointerdown: Default::default(),
|
|
202
183
|
source_dragend: Default::default(),
|
|
203
184
|
drag_target: Default::default(),
|
|
204
185
|
}));
|
|
205
186
|
|
|
187
|
+
theme.register_host_pointerdown();
|
|
206
188
|
ApiFuture::spawn(theme.clone().init());
|
|
207
189
|
theme
|
|
208
190
|
}
|
|
@@ -357,6 +339,20 @@ impl Presentation {
|
|
|
357
339
|
self.open_column_settings.borrow().deref().clone()
|
|
358
340
|
}
|
|
359
341
|
|
|
342
|
+
pub fn is_control_group_collapsed(&self, key: &str) -> bool {
|
|
343
|
+
self.collapsed_control_groups.borrow().contains(key)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
pub fn set_control_group_collapsed(&self, key: &str, collapsed: bool) {
|
|
347
|
+
if collapsed {
|
|
348
|
+
self.collapsed_control_groups
|
|
349
|
+
.borrow_mut()
|
|
350
|
+
.insert(key.to_owned());
|
|
351
|
+
} else {
|
|
352
|
+
self.collapsed_control_groups.borrow_mut().remove(key);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
360
356
|
async fn init(self) -> ApiResult<()> {
|
|
361
357
|
self.set_theme_attribute(self.get_selected_theme_name().await.as_deref())
|
|
362
358
|
}
|
|
@@ -560,18 +556,53 @@ impl Presentation {
|
|
|
560
556
|
}
|
|
561
557
|
}
|
|
562
558
|
|
|
563
|
-
|
|
559
|
+
/// Claim a `dragstart` for the column drag machinery, returning `false`
|
|
560
|
+
/// (cancelling the native drag) when it did not originate on a
|
|
561
|
+
/// `draggable="true"` row or installation failed.
|
|
562
|
+
pub fn set_drag_image(&self, event: &DragEvent) -> bool {
|
|
563
|
+
match self.try_set_drag_image(event) {
|
|
564
|
+
Ok(true) => true,
|
|
565
|
+
Ok(false) => {
|
|
566
|
+
event.prevent_default();
|
|
567
|
+
if let Err(e) = clear_document_selection() {
|
|
568
|
+
web_sys::console::warn_1(&e.into());
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
false
|
|
572
|
+
},
|
|
573
|
+
Err(e) => {
|
|
574
|
+
event.prevent_default();
|
|
575
|
+
web_sys::console::warn_1(&e.into());
|
|
576
|
+
false
|
|
577
|
+
},
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
fn try_set_drag_image(&self, event: &DragEvent) -> ApiResult<bool> {
|
|
564
582
|
event.stop_propagation();
|
|
583
|
+
let Some(original) = closest_draggable(event) else {
|
|
584
|
+
return Ok(false);
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
let is_row_drag = event
|
|
588
|
+
.target()
|
|
589
|
+
.and_then(|target| target.dyn_into::<Node>().ok())
|
|
590
|
+
.map(|target| original.is_same_node(Some(&target)))
|
|
591
|
+
.unwrap_or(false);
|
|
592
|
+
|
|
593
|
+
if !is_row_drag {
|
|
594
|
+
return Ok(false);
|
|
595
|
+
}
|
|
596
|
+
|
|
565
597
|
self.register_source_dragend(event)?;
|
|
566
598
|
if let Some(dt) = event.data_transfer() {
|
|
567
599
|
dt.set_drop_effect("move");
|
|
568
600
|
}
|
|
569
601
|
|
|
570
|
-
let original: HtmlElement = event.target().into_apierror()?.unchecked_into();
|
|
571
602
|
let elem: HtmlElement = original
|
|
572
603
|
.children()
|
|
573
604
|
.get_with_index(0)
|
|
574
|
-
.
|
|
605
|
+
.into_apierror()?
|
|
575
606
|
.clone_node_with_deep(true)?
|
|
576
607
|
.unchecked_into();
|
|
577
608
|
|
|
@@ -595,7 +626,7 @@ impl Presentation {
|
|
|
595
626
|
Ok(())
|
|
596
627
|
});
|
|
597
628
|
|
|
598
|
-
Ok(
|
|
629
|
+
Ok(true)
|
|
599
630
|
}
|
|
600
631
|
|
|
601
632
|
/// Is the drag/drop state currently in `action`?
|
|
@@ -678,6 +709,22 @@ impl Presentation {
|
|
|
678
709
|
Ok(())
|
|
679
710
|
}
|
|
680
711
|
|
|
712
|
+
fn register_host_pointerdown(&self) {
|
|
713
|
+
let closure = Closure::wrap(Box::new(move |event: PointerEvent| {
|
|
714
|
+
if closest_draggable(&event).is_some()
|
|
715
|
+
&& let Err(e) = clear_document_selection()
|
|
716
|
+
{
|
|
717
|
+
web_sys::console::warn_1(&e.into());
|
|
718
|
+
}
|
|
719
|
+
}) as Box<dyn FnMut(PointerEvent)>);
|
|
720
|
+
|
|
721
|
+
self.viewer_elem
|
|
722
|
+
.add_event_listener_with_callback("pointerdown", closure.as_ref().unchecked_ref())
|
|
723
|
+
.unwrap();
|
|
724
|
+
|
|
725
|
+
*self.host_pointerdown.borrow_mut() = Some(closure);
|
|
726
|
+
}
|
|
727
|
+
|
|
681
728
|
fn register_host_dragend(&self) {
|
|
682
729
|
if let Some(prev) = self.host_dragend.borrow_mut().take() {
|
|
683
730
|
let _ = self
|
|
@@ -0,0 +1,38 @@
|
|
|
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 crate::workspace::Workspace;
|
|
14
|
+
|
|
15
|
+
/// Every hosted `Table` name per loaded `Client`, as `(client name, sorted
|
|
16
|
+
/// table names)` in registration order.
|
|
17
|
+
pub async fn fetch_hosted_tables(workspace: &Workspace) -> Vec<(String, Vec<String>)> {
|
|
18
|
+
let clients = workspace.clients();
|
|
19
|
+
let mut tables = Vec::with_capacity(clients.len());
|
|
20
|
+
for client in clients {
|
|
21
|
+
let mut names = match client.get_hosted_table_names().await {
|
|
22
|
+
Ok(names) => names,
|
|
23
|
+
Err(err) => {
|
|
24
|
+
tracing::warn!(
|
|
25
|
+
"Failed to list tables for `Client` \"{}\": {err}",
|
|
26
|
+
client.get_name()
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
vec![]
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
names.sort_unstable();
|
|
34
|
+
tables.push((client.get_name().to_owned(), names));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
tables
|
|
38
|
+
}
|
package/src/rust/queries/mod.rs
CHANGED
|
@@ -28,6 +28,7 @@ pub mod export_app;
|
|
|
28
28
|
mod exports;
|
|
29
29
|
mod fetch_column_stats;
|
|
30
30
|
mod get_viewer_config;
|
|
31
|
+
mod hosted_tables;
|
|
31
32
|
mod is_invalid_drop;
|
|
32
33
|
mod palette_set;
|
|
33
34
|
mod plugin_column_styles;
|
|
@@ -40,6 +41,7 @@ pub use self::export_app::*;
|
|
|
40
41
|
pub use self::exports::*;
|
|
41
42
|
pub use self::fetch_column_stats::*;
|
|
42
43
|
pub use self::get_viewer_config::*;
|
|
44
|
+
pub use self::hosted_tables::*;
|
|
43
45
|
pub use self::is_invalid_drop::*;
|
|
44
46
|
pub use self::palette_set::*;
|
|
45
47
|
pub use self::plugin_column_styles::*;
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
use std::collections::BTreeMap;
|
|
14
14
|
|
|
15
|
-
use crate::config::{CssKind, CssLiteralUse, NamedValue, assign_palette_names};
|
|
16
15
|
use crate::presentation::Presentation;
|
|
16
|
+
use crate::utils::{CssKind, CssLiteralUse, NamedValue, assign_palette_names};
|
|
17
17
|
use crate::workspace::{PanelId, Workspace};
|
|
18
18
|
|
|
19
19
|
/// Every CSS literal in use across the workspace, as `(panel id, use)`,
|
|
@@ -42,7 +42,7 @@ pub fn get_plugin_config_schema(
|
|
|
42
42
|
wasm_bindgen::JsValue::from_serde_ext(view_config).unwrap_or(wasm_bindgen::JsValue::NULL);
|
|
43
43
|
let raw = plugin._plugin_config_schema(&view_config_js)?;
|
|
44
44
|
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
45
|
-
.map(|schema| schema.
|
|
45
|
+
.map(|schema| schema.canonicalize())
|
|
46
46
|
.map_err(|e| e.into())
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -100,6 +100,6 @@ pub fn get_column_config_schema(
|
|
|
100
100
|
)?;
|
|
101
101
|
|
|
102
102
|
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
103
|
-
.map(|schema| schema.
|
|
103
|
+
.map(|schema| schema.canonicalize())
|
|
104
104
|
.map_err(|e| e.into())
|
|
105
105
|
}
|
|
@@ -51,9 +51,7 @@ pub fn mount_plugin(
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/// Given an async `task` which draws `plugin`, [`mount_plugin`] it, then run
|
|
54
|
-
/// `task`.
|
|
55
|
-
/// outside a locked run does not compile (invariants I3/I6 of the merge;
|
|
56
|
-
/// see `SESSION_CONFIG_COHERENCE_PLAN.md`).
|
|
54
|
+
/// `task`.
|
|
57
55
|
pub async fn activate_plugin<T>(
|
|
58
56
|
_guard: &RenderGuard,
|
|
59
57
|
viewer: &HtmlElement,
|
|
@@ -19,6 +19,7 @@ use std::future::Future;
|
|
|
19
19
|
use std::rc::Rc;
|
|
20
20
|
|
|
21
21
|
use perspective_client::View;
|
|
22
|
+
use perspective_client::proto::ViewDimensionsResp;
|
|
22
23
|
use perspective_js::utils::{ApiResult, ResultTApiErrorExt};
|
|
23
24
|
use wasm_bindgen::prelude::*;
|
|
24
25
|
use web_sys::*;
|
|
@@ -44,25 +45,27 @@ impl Renderer {
|
|
|
44
45
|
let _ = el.class_list().toggle_with_force("multi", !is_solo);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
/// Stamp this panel's effective `theme` attribute.
|
|
48
48
|
pub fn stamp_theme(&self, plugin: Option<&JsPerspectiveViewerPlugin>) {
|
|
49
|
+
let theme = self.theme();
|
|
49
50
|
let active_plugin = self.active_plugin();
|
|
50
51
|
if let Some(plugin) = plugin.or(active_plugin.as_ref()) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let _ = theme_elem.set_attribute("theme", &theme);
|
|
57
|
-
},
|
|
58
|
-
Some(_) => {},
|
|
59
|
-
None => {
|
|
60
|
-
let _ = theme_elem.remove_attribute("theme");
|
|
61
|
-
},
|
|
62
|
-
}
|
|
52
|
+
stamp_theme_attr(plugin.unchecked_ref::<Element>(), theme.as_deref());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if let Some(tab) = self.tab_host() {
|
|
56
|
+
stamp_theme_attr(&tab, theme.as_deref());
|
|
63
57
|
}
|
|
64
58
|
}
|
|
65
59
|
|
|
60
|
+
fn tab_host(&self) -> Option<Element> {
|
|
61
|
+
let slot = format!("tab-{}", self.slot_name()?);
|
|
62
|
+
let viewer = self.plugin_data.borrow().viewer_elem.clone();
|
|
63
|
+
let children = viewer.children();
|
|
64
|
+
(0..children.length())
|
|
65
|
+
.filter_map(|i| children.item(i))
|
|
66
|
+
.find(|el| el.get_attribute("slot").as_deref() == Some(slot.as_str()))
|
|
67
|
+
}
|
|
68
|
+
|
|
66
69
|
/// Restyle the active plugin and re-draw.
|
|
67
70
|
pub async fn restyle_all(&self) -> ApiResult<JsValue> {
|
|
68
71
|
self.render_task(|guard| async move {
|
|
@@ -78,8 +81,14 @@ impl Renderer {
|
|
|
78
81
|
self.stamp_active(&plugin);
|
|
79
82
|
plugin.restyle();
|
|
80
83
|
*self.0.captured_theme.borrow_mut() = Some(stamped_theme);
|
|
81
|
-
let
|
|
82
|
-
|
|
84
|
+
let dimensions = ctx.view.dimensions().await?;
|
|
85
|
+
let mut limits = get_row_and_col_limits(
|
|
86
|
+
&dimensions,
|
|
87
|
+
&ctx.view,
|
|
88
|
+
&meta,
|
|
89
|
+
self.is_render_warning_enabled(),
|
|
90
|
+
)
|
|
91
|
+
.await?;
|
|
83
92
|
|
|
84
93
|
limits.is_update = true;
|
|
85
94
|
plugin
|
|
@@ -165,10 +174,8 @@ impl Renderer {
|
|
|
165
174
|
width: f64,
|
|
166
175
|
height: f64,
|
|
167
176
|
) -> ApiResult<Option<js_sys::Function>> {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
self.0.presize_pending.set(self.0.presize_pending.get() - 1);
|
|
171
|
-
result
|
|
177
|
+
let _pending = PresizePendingGuard::increment(self);
|
|
178
|
+
self.presize_inner(None, width, height).await
|
|
172
179
|
}
|
|
173
180
|
|
|
174
181
|
/// Pre-size AND pre-position the plugin to its target layout bo.
|
|
@@ -179,10 +186,8 @@ impl Renderer {
|
|
|
179
186
|
width: f64,
|
|
180
187
|
height: f64,
|
|
181
188
|
) -> ApiResult<Option<js_sys::Function>> {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
self.0.presize_pending.set(self.0.presize_pending.get() - 1);
|
|
185
|
-
result
|
|
189
|
+
let _pending = PresizePendingGuard::increment(self);
|
|
190
|
+
self.presize_inner(Some((dx, dy)), width, height).await
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
async fn presize_inner(
|
|
@@ -219,6 +224,7 @@ impl Renderer {
|
|
|
219
224
|
};
|
|
220
225
|
|
|
221
226
|
self.stamp_active(&jsplugin);
|
|
227
|
+
self.0.presized_box.set(None);
|
|
222
228
|
jsplugin.resize().await?;
|
|
223
229
|
Ok(None)
|
|
224
230
|
},
|
|
@@ -245,7 +251,33 @@ impl Renderer {
|
|
|
245
251
|
self.stamp_active(&plugin);
|
|
246
252
|
self.clear_presize()?;
|
|
247
253
|
if plugin.capabilities().presize {
|
|
248
|
-
|
|
254
|
+
let Some(present) = plugin.presize(width, height).await? else {
|
|
255
|
+
return Ok(None);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
self.0.presized_box.set(Some((width, height)));
|
|
259
|
+
let inline = js_sys::Reflect::get(&present, &"inline".into())
|
|
260
|
+
.ok()
|
|
261
|
+
.and_then(|v| v.as_bool())
|
|
262
|
+
.unwrap_or(false);
|
|
263
|
+
|
|
264
|
+
if inline {
|
|
265
|
+
self.0.presized_box.set(None);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let renderer = self.clone();
|
|
269
|
+
let wrapped = Closure::once_into_js(move || {
|
|
270
|
+
let filled = present
|
|
271
|
+
.call0(&JsValue::NULL)
|
|
272
|
+
.map(|v| v.as_bool() != Some(false))
|
|
273
|
+
.unwrap_or(false);
|
|
274
|
+
|
|
275
|
+
if !filled {
|
|
276
|
+
renderer.0.presized_box.set(None);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return Ok(Some(wrapped.unchecked_into::<js_sys::Function>()));
|
|
249
281
|
}
|
|
250
282
|
|
|
251
283
|
let main_panel: &web_sys::HtmlElement = plugin.unchecked_ref();
|
|
@@ -282,6 +314,7 @@ impl Renderer {
|
|
|
282
314
|
}
|
|
283
315
|
|
|
284
316
|
result?;
|
|
317
|
+
self.0.presized_box.set(Some((width, height)));
|
|
285
318
|
}
|
|
286
319
|
|
|
287
320
|
Ok(None)
|
|
@@ -311,14 +344,16 @@ impl Renderer {
|
|
|
311
344
|
pub async fn draw_fresh(&self, guard: &RenderGuard, view: FreshView) -> ApiResult<()> {
|
|
312
345
|
let timer = self.render_timer();
|
|
313
346
|
timer
|
|
314
|
-
.capture_time(self.draw_view(guard, view.view(), false))
|
|
347
|
+
.capture_time(self.draw_view(guard, view.view(), false, None))
|
|
315
348
|
.await
|
|
316
349
|
}
|
|
317
350
|
|
|
318
351
|
/// Repaint the ALREADY-BOUND `view` on the active plugin.
|
|
319
352
|
pub async fn update_bound(&self, guard: &RenderGuard, view: &View) -> ApiResult<()> {
|
|
320
353
|
let timer = self.render_timer();
|
|
321
|
-
timer
|
|
354
|
+
timer
|
|
355
|
+
.capture_time(self.draw_view(guard, view, true, None))
|
|
356
|
+
.await
|
|
322
357
|
}
|
|
323
358
|
|
|
324
359
|
/// Mint the [`FreshView`] full-draw witness for a plugin that has never
|
|
@@ -367,26 +402,33 @@ impl Renderer {
|
|
|
367
402
|
/// Redraw an already-bound view, debounced.
|
|
368
403
|
pub async fn update_lazy(
|
|
369
404
|
&self,
|
|
370
|
-
view: impl Future<Output = ApiResult<Option<View>>>,
|
|
405
|
+
view: impl Future<Output = ApiResult<Option<(View, Option<ViewDimensionsResp>)>>>,
|
|
371
406
|
) -> ApiResult<()> {
|
|
372
407
|
let timer = self.render_timer();
|
|
373
408
|
self.draw_lock()
|
|
374
409
|
.debounce_with(|guard| async move {
|
|
375
410
|
set_timeout(timer.get_throttle()).await?;
|
|
376
411
|
if self.0.presize_pending.get() > 0 {
|
|
412
|
+
// Record the drop like the hidden-panel path
|
|
413
|
+
// (`wire_panel_render_sub`) does — a silently
|
|
414
|
+
// discarded update otherwise leaves the plugin's
|
|
415
|
+
// retained frame stale until the NEXT data tick,
|
|
416
|
+
// or forever if none comes. `draw_view` /
|
|
417
|
+
// `activation_render` consume the marker.
|
|
377
418
|
tracing::debug!("Update skipped, presize pending");
|
|
419
|
+
self.set_data_stale();
|
|
378
420
|
return Ok(());
|
|
379
421
|
}
|
|
380
422
|
|
|
381
423
|
self.mount_active_plugin()?;
|
|
382
|
-
if let Some(view) = view.await? {
|
|
424
|
+
if let Some((view, dimensions)) = view.await? {
|
|
383
425
|
let _pin = self
|
|
384
426
|
.cached_context()
|
|
385
427
|
.map(|ctx| self.pin_context(&guard, ctx));
|
|
386
428
|
|
|
387
429
|
let timer = self.render_timer();
|
|
388
430
|
timer
|
|
389
|
-
.capture_time(self.draw_view(&guard, &view, true))
|
|
431
|
+
.capture_time(self.draw_view(&guard, &view, true, dimensions))
|
|
390
432
|
.await
|
|
391
433
|
} else {
|
|
392
434
|
tracing::debug!("Render skipped, no `View` attached");
|
|
@@ -396,11 +438,14 @@ impl Renderer {
|
|
|
396
438
|
.await
|
|
397
439
|
}
|
|
398
440
|
|
|
441
|
+
/// `dimensions` is the caller's already-fetched view dimensions, or
|
|
442
|
+
/// `None` to fetch here.
|
|
399
443
|
async fn draw_view(
|
|
400
444
|
&self,
|
|
401
445
|
_guard: &RenderGuard,
|
|
402
446
|
view: &perspective_client::View,
|
|
403
447
|
is_update: bool,
|
|
448
|
+
dimensions: Option<ViewDimensionsResp>,
|
|
404
449
|
) -> ApiResult<()> {
|
|
405
450
|
debug_assert!(
|
|
406
451
|
!is_update || self.cached_context().is_none() || self.render_context().is_some(),
|
|
@@ -417,8 +462,14 @@ impl Renderer {
|
|
|
417
462
|
};
|
|
418
463
|
|
|
419
464
|
let meta = self.metadata();
|
|
465
|
+
let dimensions = match dimensions {
|
|
466
|
+
Some(dimensions) => dimensions,
|
|
467
|
+
None => view.dimensions().await?,
|
|
468
|
+
};
|
|
469
|
+
|
|
420
470
|
let mut limits =
|
|
421
|
-
get_row_and_col_limits(view, &meta, self.is_render_warning_enabled())
|
|
471
|
+
get_row_and_col_limits(&dimensions, view, &meta, self.is_render_warning_enabled())
|
|
472
|
+
.await?;
|
|
422
473
|
|
|
423
474
|
limits.is_update = is_update;
|
|
424
475
|
if let Some(cb) = self.0.on_render_limits_changed.borrow().as_ref() {
|
|
@@ -437,6 +488,7 @@ impl Renderer {
|
|
|
437
488
|
}
|
|
438
489
|
|
|
439
490
|
self.0.data_stale.set(false);
|
|
491
|
+
self.0.presized_box.set(None);
|
|
440
492
|
let result = if is_update {
|
|
441
493
|
let task = plugin.update(view.clone().into(), limits.max_cols, limits.max_rows, false);
|
|
442
494
|
activate_plugin(_guard, &viewer_elem, &plugin, slot.as_deref(), task).await
|
|
@@ -473,3 +525,38 @@ impl Renderer {
|
|
|
473
525
|
self.draw_lock.clone()
|
|
474
526
|
}
|
|
475
527
|
}
|
|
528
|
+
|
|
529
|
+
/// RAII increment of [`RendererData::presize_pending`], the counter that
|
|
530
|
+
/// gates [`Renderer::update_lazy`] dispatches. Decrementing on `Drop`
|
|
531
|
+
/// (not on the happy path) makes the release unconditional — a presize
|
|
532
|
+
/// future cancelled mid-await can never strand the counter above zero,
|
|
533
|
+
/// which would silently discard every subsequent debounced update for
|
|
534
|
+
/// the panel's lifetime.
|
|
535
|
+
struct PresizePendingGuard(Renderer);
|
|
536
|
+
|
|
537
|
+
impl PresizePendingGuard {
|
|
538
|
+
fn increment(renderer: &Renderer) -> Self {
|
|
539
|
+
let cell = &renderer.0.presize_pending;
|
|
540
|
+
cell.set(cell.get() + 1);
|
|
541
|
+
Self(renderer.clone())
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
impl Drop for PresizePendingGuard {
|
|
546
|
+
fn drop(&mut self) {
|
|
547
|
+
let cell = &self.0.0.presize_pending;
|
|
548
|
+
cell.set(cell.get() - 1);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
fn stamp_theme_attr(el: &Element, theme: Option<&str>) {
|
|
553
|
+
match theme {
|
|
554
|
+
Some(theme) if el.get_attribute("theme").as_deref() != Some(theme) => {
|
|
555
|
+
let _ = el.set_attribute("theme", theme);
|
|
556
|
+
},
|
|
557
|
+
Some(_) => {},
|
|
558
|
+
None => {
|
|
559
|
+
let _ = el.remove_attribute("theme");
|
|
560
|
+
},
|
|
561
|
+
}
|
|
562
|
+
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
use perspective_client::proto::ViewDimensionsResp;
|
|
13
14
|
use perspective_js::utils::ApiError;
|
|
14
15
|
|
|
15
16
|
use crate::config::PluginStaticConfig;
|
|
@@ -31,16 +32,34 @@ pub struct RenderLimits {
|
|
|
31
32
|
pub max_rows: Option<usize>,
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
///
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
impl RenderLimits {
|
|
36
|
+
/// The column cap is in effect (fewer columns render than the view has).
|
|
37
|
+
pub fn is_col_capped(&self) -> bool {
|
|
38
|
+
self.max_cols.is_some_and(|x| x < self.num_cols)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// The row cap is in effect (fewer rows render than the view has).
|
|
42
|
+
pub fn is_row_capped(&self) -> bool {
|
|
43
|
+
self.max_rows.is_some_and(|x| x < self.num_rows)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Either cap is in effect — exactly when the render warning shows.
|
|
47
|
+
pub fn is_capped(&self) -> bool {
|
|
48
|
+
self.is_col_capped() || self.is_row_capped()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Compute the row/column caps for `view` from its already-fetched
|
|
53
|
+
/// `dimensions` against the plugin's static limits. `render_warning` is the
|
|
54
|
+
/// renderer-state flag controlling whether the warning is currently armed;
|
|
55
|
+
/// when `false`, an oversized view renders uncapped (the user has dismissed
|
|
56
|
+
/// the warning).
|
|
38
57
|
pub async fn get_row_and_col_limits(
|
|
58
|
+
dimensions: &ViewDimensionsResp,
|
|
39
59
|
view: &perspective_client::View,
|
|
40
60
|
config: &PluginStaticConfig,
|
|
41
61
|
render_warning: bool,
|
|
42
62
|
) -> Result<RenderLimits, ApiError> {
|
|
43
|
-
let dimensions = view.dimensions().await?;
|
|
44
63
|
let num_cols = dimensions.num_view_columns as usize;
|
|
45
64
|
let num_rows = dimensions.num_view_rows as usize;
|
|
46
65
|
match (config.max_columns, render_warning) {
|