@perspective-dev/viewer 4.5.2 → 5.1.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 +20 -1
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.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/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/monokai.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/esm/extensions.d.ts +22 -3
- package/dist/esm/perspective-viewer.d.ts +9 -0
- package/dist/esm/perspective-viewer.inline.js +20 -1
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +20 -1
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/plugin.d.ts +71 -5
- package/dist/esm/ts-rs/ClientOptions.d.ts +7 -0
- package/dist/esm/ts-rs/ExportMethod.d.ts +1 -0
- package/dist/esm/ts-rs/ExportOptions.d.ts +9 -0
- package/dist/esm/ts-rs/GetClientOptions.d.ts +8 -0
- package/dist/esm/ts-rs/GetTableOptions.d.ts +8 -0
- package/dist/esm/ts-rs/Layout.d.ts +11 -0
- package/dist/esm/ts-rs/Orientation.d.ts +4 -0
- package/dist/esm/ts-rs/PanelOptions.d.ts +7 -0
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +41 -0
- package/dist/esm/ts-rs/SplitLayout.d.ts +7 -0
- package/dist/esm/ts-rs/TabLayout.d.ts +10 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +8 -2
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +9 -0
- package/dist/esm/ts-rs/WindowAggregate.d.ts +1 -0
- package/dist/esm/ts-rs/WindowSort.d.ts +7 -0
- package/dist/esm/ts-rs/WindowSortDir.d.ts +6 -0
- package/dist/esm/ts-rs/WindowSpec.d.ts +46 -0
- package/dist/esm/ts-rs/Windows.d.ts +10 -0
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +40 -0
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +28 -0
- package/dist/wasm/perspective-viewer.d.ts +299 -78
- package/dist/wasm/perspective-viewer.js +544 -186
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +34 -26
- package/dist/wasm/snippets/perspective-viewer-8a8c7d5d696b43ae/inline0.js +6 -0
- package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline3.js → perspective-viewer-8a8c7d5d696b43ae/inline4.js} +5 -0
- package/package.json +3 -2
- package/src/css/_context_menu.css +14 -0
- package/src/css/column-selector.css +1 -0
- package/src/css/column-settings-panel.css +68 -3
- package/src/css/column-style.css +4 -3
- package/src/css/config-selector.css +22 -1
- package/src/css/containers/context-menu.css +107 -0
- package/src/css/containers/tabs.css +2 -2
- package/src/css/panel-tab.css +193 -0
- package/src/css/plugin-selector.css +7 -10
- package/src/css/status-bar.css +75 -81
- package/src/css/viewer.css +188 -70
- package/src/rust/components/column_dropdown.rs +40 -30
- package/src/rust/components/column_selector/active_column.rs +28 -45
- package/src/rust/components/column_selector/aggregate_selector.rs +4 -7
- package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
- package/src/rust/components/column_selector/config_selector.rs +40 -82
- package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
- package/src/rust/components/column_selector/filter_column.rs +5 -15
- package/src/rust/components/column_selector/inactive_column.rs +11 -9
- package/src/rust/components/column_selector/sort_column.rs +3 -6
- package/src/rust/components/column_selector.rs +25 -14
- package/src/rust/components/column_settings_sidebar/style_tab.rs +2 -1
- package/src/rust/components/column_settings_sidebar/window_tab.rs +34 -0
- package/src/rust/components/column_settings_sidebar.rs +163 -16
- package/src/rust/components/containers/dragdrop_list.rs +73 -3
- package/src/rust/components/containers/sidebar.rs +99 -22
- package/src/rust/components/containers/split_panel.rs +181 -62
- package/src/rust/components/context_menu.rs +130 -0
- package/src/rust/components/editable_header.rs +2 -0
- package/src/rust/components/form/debug.rs +3 -0
- package/src/rust/components/global_filter_bar.rs +76 -0
- package/src/rust/components/main_panel/frame_theme.rs +186 -0
- package/src/rust/components/main_panel/msg.rs +62 -0
- package/src/rust/components/main_panel/presize.rs +220 -0
- package/src/rust/components/main_panel/reconcile.rs +224 -0
- package/src/rust/components/main_panel/render.rs +385 -0
- package/src/rust/components/main_panel/update.rs +184 -0
- package/src/rust/components/main_panel.rs +316 -71
- package/src/rust/components/mod.rs +5 -0
- package/src/rust/components/panel_menu.rs +493 -0
- package/src/rust/components/panel_tab.rs +510 -0
- package/src/rust/components/plugin_tab.rs +1 -1
- package/src/rust/components/portal.rs +7 -0
- package/src/rust/components/settings_panel.rs +13 -5
- package/src/rust/components/status_bar.rs +110 -178
- package/src/rust/components/status_indicator.rs +6 -9
- package/src/rust/components/style/mod.rs +5 -0
- package/src/rust/components/style/style_provider.rs +2 -0
- package/src/rust/components/viewer/filters.rs +133 -0
- package/src/rust/components/viewer/msg.rs +182 -0
- package/src/rust/components/viewer/panels.rs +326 -0
- package/src/rust/components/viewer/render.rs +255 -0
- package/src/rust/components/viewer/settings.rs +434 -0
- package/src/rust/components/viewer/snapshots.rs +134 -0
- package/src/rust/components/viewer/wiring.rs +505 -0
- package/src/rust/components/viewer.rs +245 -756
- package/src/rust/components/window_editor.rs +1013 -0
- package/src/rust/config/export_method.rs +1 -1
- package/src/rust/config/mod.rs +4 -0
- package/src/rust/config/options.rs +71 -0
- package/src/rust/config/viewer_config.rs +24 -2
- package/src/rust/config/workspace_config.rs +92 -0
- package/src/rust/custom_elements/copy_dropdown.rs +3 -1
- package/src/rust/custom_elements/debug_plugin.rs +20 -1
- package/src/rust/custom_elements/export_dropdown.rs +3 -1
- package/src/rust/custom_elements/viewer.rs +1015 -286
- package/src/rust/custom_events.rs +212 -61
- package/src/rust/js/mod.rs +2 -0
- package/src/rust/js/plugin.rs +297 -10
- package/src/rust/js/regular_layout.rs +258 -0
- package/src/rust/lib.rs +24 -2
- package/src/rust/presentation/column_locator.rs +16 -1
- package/src/rust/presentation.rs +66 -33
- package/src/rust/queries/columns_iter_set.rs +7 -1
- package/src/rust/queries/get_viewer_config.rs +29 -11
- package/src/rust/queries/plugin_column_styles.rs +2 -2
- package/src/rust/renderer/activate.rs +41 -14
- package/src/rust/renderer/dispatch.rs +463 -0
- package/src/rust/renderer/plugin_config.rs +495 -0
- package/src/rust/renderer/registry.rs +18 -6
- package/src/rust/renderer/render_timer.rs +29 -10
- package/src/rust/renderer.rs +384 -838
- package/src/rust/session/column_defaults_update.rs +26 -8
- package/src/rust/session/drag_drop_update.rs +18 -0
- package/src/rust/session/metadata.rs +66 -9
- package/src/rust/session/replace_expression_update.rs +1 -0
- package/src/rust/session/view_subscription.rs +30 -15
- package/src/rust/session.rs +755 -264
- package/src/rust/tasks/apply_global_filters.rs +89 -0
- package/src/rust/tasks/auto_pause.rs +171 -0
- package/src/rust/tasks/copy_export.rs +29 -19
- package/src/rust/tasks/create_panel.rs +243 -0
- package/src/rust/tasks/dismiss_render_warning.rs +3 -1
- package/src/rust/tasks/edit_expression.rs +4 -4
- package/src/rust/tasks/edit_window.rs +155 -0
- package/src/rust/tasks/eject.rs +64 -23
- package/src/rust/tasks/mod.rs +30 -4
- package/src/rust/tasks/pipeline.rs +411 -0
- package/src/rust/tasks/presize_panels.rs +324 -0
- package/src/rust/tasks/reset_all.rs +22 -12
- package/src/rust/tasks/resize_observer.rs +124 -87
- package/src/rust/tasks/restore_and_render.rs +80 -86
- package/src/rust/tasks/restore_panel.rs +145 -0
- package/src/rust/tasks/send_column_config.rs +5 -2
- package/src/rust/tasks/send_plugin_config.rs +5 -2
- package/src/rust/tasks/set_edit_mode.rs +59 -0
- package/src/rust/tasks/sync_update_panels.rs +152 -0
- package/src/rust/tasks/table_lifecycle.rs +158 -0
- package/src/rust/tasks/update_theme.rs +56 -12
- package/src/rust/utils/browser/dragdrop.rs +12 -0
- package/src/rust/utils/browser/tests/debounce.rs +49 -0
- package/src/rust/utils/completion.rs +56 -0
- package/src/rust/utils/custom_element.rs +5 -0
- package/src/rust/utils/debounce.rs +189 -17
- package/src/rust/utils/mod.rs +6 -0
- package/src/rust/utils/pending_effects.rs +62 -0
- package/src/rust/utils/spawn.rs +33 -0
- package/src/rust/utils/weak_scope.rs +0 -10
- package/src/rust/workspace.rs +846 -0
- package/src/svg/active-indicator.svg +3 -0
- package/src/svg/pin-icon.svg +5 -0
- package/src/themes/botanical.css +5 -2
- package/src/themes/defaults.css +54 -2
- package/src/themes/dracula.css +6 -3
- package/src/themes/gruvbox-dark.css +5 -2
- package/src/themes/gruvbox.css +5 -2
- package/src/themes/icons.css +5 -2
- package/src/themes/intl/de.css +7 -0
- package/src/themes/intl/es.css +7 -0
- package/src/themes/intl/fr.css +7 -0
- package/src/themes/intl/ja.css +7 -0
- package/src/themes/intl/pt.css +7 -0
- package/src/themes/intl/zh.css +7 -0
- package/src/themes/intl.css +10 -1
- package/src/themes/monokai.css +5 -2
- package/src/themes/phosphor.css +5 -2
- package/src/themes/pro-dark.css +5 -2
- package/src/themes/pro.css +2 -1
- package/src/themes/solarized-dark.css +5 -2
- package/src/themes/solarized.css +5 -2
- package/src/themes/vaporwave.css +5 -2
- package/src/ts/bootstrap.ts +8 -7
- package/src/ts/extensions.ts +30 -2
- package/src/ts/perspective-viewer.ts +10 -0
- package/src/ts/plugin.ts +72 -5
- package/src/ts/ts-rs/ClientOptions.ts +7 -0
- package/src/ts/ts-rs/ExportMethod.ts +3 -0
- package/src/ts/ts-rs/ExportOptions.ts +8 -0
- package/src/ts/ts-rs/GetClientOptions.ts +7 -0
- package/src/ts/ts-rs/GetTableOptions.ts +7 -0
- package/src/ts/ts-rs/Layout.ts +9 -0
- package/src/ts/ts-rs/Orientation.ts +6 -0
- package/src/ts/ts-rs/PanelOptions.ts +7 -0
- package/src/ts/ts-rs/PanelViewerConfig.ts +16 -0
- package/src/ts/ts-rs/SplitLayout.ts +5 -0
- package/src/ts/ts-rs/TabLayout.ts +11 -0
- package/src/ts/ts-rs/ViewerConfig.ts +7 -2
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +9 -0
- package/src/ts/ts-rs/WindowAggregate.ts +3 -0
- package/src/ts/ts-rs/WindowSort.ts +9 -0
- package/src/ts/ts-rs/WindowSortDir.ts +8 -0
- package/src/ts/ts-rs/WindowSpec.ts +46 -0
- package/src/ts/ts-rs/Windows.ts +10 -0
- package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
- package/src/rust/tasks/intersection_observer.rs +0 -98
- package/src/rust/tasks/update_and_render.rs +0 -110
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline0.js → perspective-viewer-8a8c7d5d696b43ae/inline1.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline1.js → perspective-viewer-8a8c7d5d696b43ae/inline2.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline2.js → perspective-viewer-8a8c7d5d696b43ae/inline3.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline4.js → perspective-viewer-8a8c7d5d696b43ae/inline5.js} +0 -0
package/src/rust/lib.rs
CHANGED
|
@@ -23,10 +23,13 @@
|
|
|
23
23
|
#![warn(
|
|
24
24
|
clippy::all,
|
|
25
25
|
clippy::panic_in_result_fn,
|
|
26
|
-
clippy::await_holding_refcell_ref,
|
|
27
26
|
clippy::fallible_impl_from,
|
|
28
27
|
clippy::unneeded_field_pattern
|
|
29
28
|
)]
|
|
29
|
+
// Holding a `Ref` across an `await` is the sibling footgun of the config
|
|
30
|
+
// write-back race this crate was rearchitected against (see
|
|
31
|
+
// `SESSION_CONFIG_COHERENCE_PLAN.md`) — deny, don't warn.
|
|
32
|
+
#![deny(clippy::await_holding_refcell_ref)]
|
|
30
33
|
|
|
31
34
|
pub mod components;
|
|
32
35
|
pub mod config;
|
|
@@ -45,6 +48,7 @@ mod session;
|
|
|
45
48
|
#[doc(hidden)]
|
|
46
49
|
pub mod tasks;
|
|
47
50
|
pub mod utils;
|
|
51
|
+
mod workspace;
|
|
48
52
|
|
|
49
53
|
#[macro_use]
|
|
50
54
|
extern crate macro_rules_attribute;
|
|
@@ -67,21 +71,39 @@ import type {
|
|
|
67
71
|
ColumnType,
|
|
68
72
|
TableInitOptions,
|
|
69
73
|
ColumnWindow,
|
|
70
|
-
ViewWindow,
|
|
74
|
+
ViewWindow,
|
|
71
75
|
TypedArrayWindow,
|
|
72
76
|
OnUpdateOptions,
|
|
73
77
|
JoinOptions,
|
|
74
78
|
UpdateOptions,
|
|
75
79
|
DeleteOptions,
|
|
80
|
+
ViewConfig,
|
|
76
81
|
ViewConfigUpdate,
|
|
77
82
|
SystemInfo,
|
|
83
|
+
Scalar,
|
|
84
|
+
Features,
|
|
78
85
|
} from "@perspective-dev/client";
|
|
79
86
|
|
|
80
87
|
export type * from "../../src/ts/ts-rs/ViewerConfig.d.ts";
|
|
81
88
|
export type * from "../../src/ts/ts-rs/ViewerConfigUpdate.d.ts";
|
|
82
89
|
export type * from "../../src/ts/ts-rs/PluginStaticConfig.d.ts";
|
|
90
|
+
export type * from "../../src/ts/ts-rs/WorkspaceConfig.d.ts";
|
|
91
|
+
export type * from "../../src/ts/ts-rs/WorkspaceConfigUpdate.d.ts";
|
|
92
|
+
export type * from "../../src/ts/ts-rs/ExportMethod.d.ts";
|
|
93
|
+
export type * from "../../src/ts/ts-rs/PanelOptions.d.ts";
|
|
94
|
+
export type * from "../../src/ts/ts-rs/ClientOptions.d.ts";
|
|
95
|
+
export type * from "../../src/ts/ts-rs/ExportOptions.d.ts";
|
|
96
|
+
export type * from "../../src/ts/ts-rs/GetTableOptions.d.ts";
|
|
97
|
+
export type * from "../../src/ts/ts-rs/GetClientOptions.d.ts";
|
|
98
|
+
import type {GetTableOptions} from "../../src/ts/ts-rs/GetTableOptions.d.ts";
|
|
99
|
+
import type {PanelOptions} from "../../src/ts/ts-rs/PanelOptions.d.ts";
|
|
100
|
+
import type {ExportOptions} from "../../src/ts/ts-rs/ExportOptions.d.ts";
|
|
101
|
+
import type {GetClientOptions} from "../../src/ts/ts-rs/GetClientOptions.d.ts";
|
|
102
|
+
import type {ClientOptions} from "../../src/ts/ts-rs/ClientOptions.d.ts";
|
|
83
103
|
import type {ViewerConfig} from "../../src/ts/ts-rs/ViewerConfig.d.ts";
|
|
84
104
|
import type {ViewerConfigUpdate} from "../../src/ts/ts-rs/ViewerConfigUpdate.d.ts";
|
|
105
|
+
import type {WorkspaceConfig} from "../../src/ts/ts-rs/WorkspaceConfig.d.ts";
|
|
106
|
+
import type {WorkspaceConfigUpdate} from "../../src/ts/ts-rs/WorkspaceConfigUpdate.d.ts";
|
|
85
107
|
"#;
|
|
86
108
|
|
|
87
109
|
/// Register a plugin globally.
|
|
@@ -19,6 +19,7 @@ use std::fmt::Display;
|
|
|
19
19
|
pub enum ColumnLocator {
|
|
20
20
|
Table(String),
|
|
21
21
|
Expression(String),
|
|
22
|
+
Window(String),
|
|
22
23
|
NewExpression,
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -28,7 +29,7 @@ impl ColumnLocator {
|
|
|
28
29
|
/// function will return None.
|
|
29
30
|
pub fn name(&self) -> Option<&String> {
|
|
30
31
|
match self {
|
|
31
|
-
Self::Table(s) | Self::Expression(s) => Some(s),
|
|
32
|
+
Self::Table(s) | Self::Expression(s) | Self::Window(s) => Some(s),
|
|
32
33
|
Self::NewExpression => None,
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -50,6 +51,19 @@ impl ColumnLocator {
|
|
|
50
51
|
pub fn is_new_expr(&self) -> bool {
|
|
51
52
|
matches!(self, ColumnLocator::NewExpression)
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
#[inline(always)]
|
|
56
|
+
pub fn is_saved_window(&self) -> bool {
|
|
57
|
+
matches!(self, ColumnLocator::Window(_))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#[inline(always)]
|
|
61
|
+
pub fn is_window_editable(&self) -> bool {
|
|
62
|
+
matches!(
|
|
63
|
+
self,
|
|
64
|
+
ColumnLocator::Window(_) | ColumnLocator::NewExpression
|
|
65
|
+
)
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
|
@@ -57,6 +71,7 @@ impl ColumnLocator {
|
|
|
57
71
|
pub enum ColumnSettingsTab {
|
|
58
72
|
#[default]
|
|
59
73
|
Attributes,
|
|
74
|
+
Window,
|
|
60
75
|
Style,
|
|
61
76
|
}
|
|
62
77
|
|
package/src/rust/presentation.rs
CHANGED
|
@@ -33,15 +33,6 @@ pub use self::drag_helpers::{DragDropContainer, DragEndCallback};
|
|
|
33
33
|
pub use self::props::{DragDropProps, PresentationProps};
|
|
34
34
|
use crate::utils::*;
|
|
35
35
|
|
|
36
|
-
/// The available themes as detected in the browser environment or set
|
|
37
|
-
/// explicitly when CORS prevents detection. Detection is expensive and
|
|
38
|
-
/// typically must be performed only once, when `document.styleSheets` is
|
|
39
|
-
/// up-to-date.
|
|
40
|
-
#[derive(Default)]
|
|
41
|
-
struct ThemeData {
|
|
42
|
-
themes: Option<Vec<String>>,
|
|
43
|
-
}
|
|
44
|
-
|
|
45
36
|
#[derive(Clone, Debug)]
|
|
46
37
|
struct DragFrom {
|
|
47
38
|
column: String,
|
|
@@ -71,7 +62,23 @@ impl DragState {
|
|
|
71
62
|
/// Actual presentations tate struct with some fields hidden.
|
|
72
63
|
pub struct PresentationHandle {
|
|
73
64
|
viewer_elem: HtmlElement,
|
|
74
|
-
|
|
65
|
+
|
|
66
|
+
/// The available themes as detected in the browser environment or set
|
|
67
|
+
/// explicitly when CORS prevents detection — a MEMO of a document
|
|
68
|
+
/// external, not component state. `None` until first parsed (detection
|
|
69
|
+
/// is expensive and must wait for `document.styleSheets`). Sync-readable
|
|
70
|
+
/// (a `RefCell`, never borrowed across an await) so synchronous paths
|
|
71
|
+
/// ("stamp with commit" — panel creation, theme mutation sites) can
|
|
72
|
+
/// derive the registry default without awaiting registry init; derived
|
|
73
|
+
/// values (e.g. the default theme = first registered) are computed from
|
|
74
|
+
/// it on demand, never mirrored.
|
|
75
|
+
themes: RefCell<Option<Vec<String>>>,
|
|
76
|
+
|
|
77
|
+
/// Single-flight guard for the stylesheet parse that populates
|
|
78
|
+
/// [`Self::themes`] (and exclusion against `reset_available_themes`) —
|
|
79
|
+
/// concurrent `get_available_themes` calls await one parse instead of
|
|
80
|
+
/// racing their own.
|
|
81
|
+
theme_init: Mutex<()>,
|
|
75
82
|
is_settings_open: RefCell<bool>,
|
|
76
83
|
open_column_settings: RefCell<OpenColumnSettings>,
|
|
77
84
|
is_workspace: RefCell<Option<bool>>,
|
|
@@ -100,13 +107,6 @@ pub struct PresentationHandle {
|
|
|
100
107
|
/// the duration of the drag.
|
|
101
108
|
drag_target: RefCell<Option<DragTargetState>>,
|
|
102
109
|
|
|
103
|
-
/// Per-element dedup cell for `perspective-config-update` event
|
|
104
|
-
/// dispatch. Read+written by `crate::custom_events::dispatch_*`
|
|
105
|
-
/// helpers; living here means every consumer with a `&Presentation`
|
|
106
|
-
/// (subscriptions in `wire_custom_events`, `tasks::send_plugin_config`,
|
|
107
|
-
/// `setSelection`) sees the same cache without separate plumbing.
|
|
108
|
-
pub last_dispatched_config: RefCell<Option<crate::config::ViewerConfig>>,
|
|
109
|
-
|
|
110
110
|
pub settings_open_changed: PubSub<bool>,
|
|
111
111
|
|
|
112
112
|
/// Injected callback from the root component, replacing the former
|
|
@@ -118,7 +118,7 @@ pub struct PresentationHandle {
|
|
|
118
118
|
pub on_eject: PubSub<()>,
|
|
119
119
|
|
|
120
120
|
/// Fires for status-bar / main-panel pointer events that target the
|
|
121
|
-
/// statusbar element. `
|
|
121
|
+
/// statusbar element. `wire_element_events` formats the `PointerEvent`'s
|
|
122
122
|
/// `type_()` into a `perspective-statusbar-{type}` `CustomEvent` name.
|
|
123
123
|
pub statusbar_pointer_event: PubSub<PointerEvent>,
|
|
124
124
|
}
|
|
@@ -148,7 +148,8 @@ impl Presentation {
|
|
|
148
148
|
pub fn new(elem: &HtmlElement) -> Self {
|
|
149
149
|
let theme = Self(Rc::new(PresentationHandle {
|
|
150
150
|
viewer_elem: elem.clone(),
|
|
151
|
-
|
|
151
|
+
themes: Default::default(),
|
|
152
|
+
theme_init: Default::default(),
|
|
152
153
|
is_workspace: Default::default(),
|
|
153
154
|
settings_open_changed: Default::default(),
|
|
154
155
|
settings_before_open_changed: Default::default(),
|
|
@@ -159,7 +160,6 @@ impl Presentation {
|
|
|
159
160
|
theme_config_updated: PubSub::default(),
|
|
160
161
|
on_eject: PubSub::default(),
|
|
161
162
|
statusbar_pointer_event: PubSub::default(),
|
|
162
|
-
last_dispatched_config: Default::default(),
|
|
163
163
|
drag_state: Default::default(),
|
|
164
164
|
drop_received: Default::default(),
|
|
165
165
|
on_dragstart: Default::default(),
|
|
@@ -259,14 +259,14 @@ impl Presentation {
|
|
|
259
259
|
/// readable stylesheets. This method is memoized - the state can be
|
|
260
260
|
/// flushed by calling `reset()`.
|
|
261
261
|
pub async fn get_available_themes(&self) -> ApiResult<PtrEqRc<Vec<String>>> {
|
|
262
|
-
let
|
|
263
|
-
if
|
|
262
|
+
let _guard = self.0.theme_init.lock().await;
|
|
263
|
+
if self.0.themes.borrow().is_none() {
|
|
264
264
|
await_dom_loaded().await?;
|
|
265
265
|
let themes = sheets::get_theme_names(&self.0.viewer_elem)?;
|
|
266
|
-
|
|
266
|
+
*self.0.themes.borrow_mut() = Some(themes);
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
Ok(
|
|
269
|
+
Ok(self.0.themes.borrow().clone().unwrap().into())
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
/// Reset the state. `styleSheets` will be re-parsed next time
|
|
@@ -281,9 +281,9 @@ impl Presentation {
|
|
|
281
281
|
.unwrap_or_default()
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
let
|
|
285
|
-
let changed = as_set(&
|
|
286
|
-
|
|
284
|
+
let _guard = self.0.theme_init.lock().await;
|
|
285
|
+
let changed = as_set(&self.0.themes.borrow()) != as_set(&themes);
|
|
286
|
+
*self.0.themes.borrow_mut() = themes;
|
|
287
287
|
changed
|
|
288
288
|
}
|
|
289
289
|
|
|
@@ -307,6 +307,22 @@ impl Presentation {
|
|
|
307
307
|
index.and_then(|x| themes.get(x).cloned())
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/// The default theme (first registered), used to resolve a panel's
|
|
311
|
+
/// effective theme when it has no per-panel override. `None` if no
|
|
312
|
+
/// themes exist.
|
|
313
|
+
pub async fn get_default_theme_name(&self) -> Option<String> {
|
|
314
|
+
self.get_available_themes().await.ok()?.first().cloned()
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/// SYNC read of the registry default theme name, derived on demand from
|
|
318
|
+
/// the memoized registry — `None` until the registry first parses (or
|
|
319
|
+
/// when no themes exist). For synchronous stamping paths that must not
|
|
320
|
+
/// await registry init; prefer [`Self::get_default_theme_name`] where
|
|
321
|
+
/// awaiting is acceptable.
|
|
322
|
+
pub fn default_theme_name_sync(&self) -> Option<String> {
|
|
323
|
+
self.0.themes.borrow().as_ref()?.first().cloned()
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
fn set_theme_attribute(&self, theme: Option<&str>) -> ApiResult<()> {
|
|
311
327
|
if let Some(theme) = theme {
|
|
312
328
|
Ok(self.0.viewer_elem.set_attribute("theme", theme)?)
|
|
@@ -325,18 +341,35 @@ impl Presentation {
|
|
|
325
341
|
|
|
326
342
|
/// Set the theme by name, or `None` for the default theme.
|
|
327
343
|
///
|
|
344
|
+
/// A NAMED theme's host attribute write is SYNCHRONOUS ("stamp with
|
|
345
|
+
/// commit") — no await separates the caller's config commit from the
|
|
346
|
+
/// attribute the document cascade styles, so a slow theme-registry
|
|
347
|
+
/// init can no longer hold the host on the former theme while e.g. an
|
|
348
|
+
/// initial `restore()`'s first draw resolves. The registry-dependent
|
|
349
|
+
/// tail (theme-list resolution + `theme_config_updated`) follows
|
|
350
|
+
/// asynchronously. `None` (reset to the registry default) still
|
|
351
|
+
/// resolves through the registry first, as the default name is not
|
|
352
|
+
/// knowable synchronously on a cold cache.
|
|
353
|
+
///
|
|
354
|
+
/// The attribute is stamped even when the requested name is not (yet)
|
|
355
|
+
/// a registered theme — matching the prior behavior for unknown
|
|
356
|
+
/// names, and additionally making an explicitly-requested name that
|
|
357
|
+
/// HAPPENS to be the registry default explicit on the element (the
|
|
358
|
+
/// old resolved-selection no-op left it absent).
|
|
359
|
+
///
|
|
328
360
|
/// # Returns
|
|
329
361
|
/// A `bool` indicating whether the internal state changed.
|
|
330
362
|
pub async fn set_theme_name(&self, theme: Option<&str>) -> ApiResult<bool> {
|
|
331
|
-
let (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
363
|
+
if let Some(theme) = theme {
|
|
364
|
+
if self.0.viewer_elem.get_attribute("theme").as_deref() == Some(theme) {
|
|
365
|
+
return Ok(false);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
self.set_theme_attribute(Some(theme))?;
|
|
336
369
|
}
|
|
337
370
|
|
|
371
|
+
let themes = self.get_available_themes().await?;
|
|
338
372
|
let index = if let Some(theme) = theme {
|
|
339
|
-
self.set_theme_attribute(Some(theme))?;
|
|
340
373
|
themes.iter().position(|x| x == theme)
|
|
341
374
|
} else if !themes.is_empty() {
|
|
342
375
|
self.set_theme_attribute(themes.first().map(|x| x.as_str()))?;
|
|
@@ -102,7 +102,8 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
102
102
|
.get_table_columns()
|
|
103
103
|
.map(|x: &Vec<String>| x.len())
|
|
104
104
|
.unwrap_or_default()
|
|
105
|
-
+ self.config.expressions.len()
|
|
105
|
+
+ self.config.expressions.len()
|
|
106
|
+
+ self.config.windows.len();
|
|
106
107
|
|
|
107
108
|
match &self.is_dragover_column {
|
|
108
109
|
Some((to_index, from_column)) => {
|
|
@@ -238,6 +239,11 @@ impl<'a> ColumnsIteratorSet<'a> {
|
|
|
238
239
|
self.order_columns(self.metadata.get_expression_columns())
|
|
239
240
|
}
|
|
240
241
|
|
|
242
|
+
/// Generate an iterator for inactive window columns.
|
|
243
|
+
pub fn window(&'a self) -> impl Iterator<Item = OrderedColumn<'a>> {
|
|
244
|
+
self.order_columns(self.metadata.get_window_columns())
|
|
245
|
+
}
|
|
246
|
+
|
|
241
247
|
/// Generate an iterator for inactive columns, which also shows the columns
|
|
242
248
|
/// in sorted order by type, then name.
|
|
243
249
|
pub fn inactive(&'a self) -> impl Iterator<Item = OrderedColumn<'a>> {
|
|
@@ -28,27 +28,45 @@ pub async fn get_viewer_config(
|
|
|
28
28
|
presentation: &Presentation,
|
|
29
29
|
) -> ApiResult<ViewerConfig> {
|
|
30
30
|
let version = config::API_VERSION.to_string();
|
|
31
|
-
let view_config = if let Some(rendered) = session.get_rendered_view_config() {
|
|
31
|
+
let mut view_config = if let Some(rendered) = session.get_rendered_view_config() {
|
|
32
32
|
(*rendered).clone()
|
|
33
33
|
} else {
|
|
34
34
|
session.get_view_config().clone()
|
|
35
35
|
};
|
|
36
|
+
|
|
37
|
+
// The rendered config may carry transient element-level global filters
|
|
38
|
+
// (master/detail) appended at view-creation; the stored `config.filter` is
|
|
39
|
+
// always the panel's own user filter, so use it here to keep `save`/
|
|
40
|
+
// `getViewConfig` free of global filters.
|
|
41
|
+
view_config.filter = session.get_view_config().filter.clone();
|
|
36
42
|
let settings = presentation.is_settings_open();
|
|
37
43
|
let plugin = renderer.metadata().name.clone();
|
|
38
44
|
let plugin_config = renderer.get_plugin_config();
|
|
39
|
-
|
|
45
|
+
// Per-panel theme: a panel's own theme takes precedence; a panel with none
|
|
46
|
+
// records the registry DEFAULT (first registered theme) it actually renders
|
|
47
|
+
// — every panel has a concrete theme and none inherits the active panel's.
|
|
48
|
+
// `None` only when no themes are registered.
|
|
49
|
+
let theme = match renderer.theme() {
|
|
50
|
+
theme @ Some(_) => theme,
|
|
51
|
+
None => presentation.get_selected_theme_name().await,
|
|
52
|
+
};
|
|
40
53
|
let title = session.get_title();
|
|
41
|
-
let table = session
|
|
54
|
+
let table = session
|
|
55
|
+
.get_table()
|
|
56
|
+
.map(|x| x.get_name().to_owned())
|
|
57
|
+
.or_else(|| session.pending_table());
|
|
42
58
|
let columns_config = renderer.all_columns_configs();
|
|
43
59
|
Ok(ViewerConfig {
|
|
44
|
-
version,
|
|
45
|
-
plugin,
|
|
46
|
-
title,
|
|
47
|
-
plugin_config,
|
|
48
|
-
columns_config,
|
|
49
60
|
settings,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
panel: PanelViewerConfig {
|
|
62
|
+
version,
|
|
63
|
+
plugin,
|
|
64
|
+
title,
|
|
65
|
+
plugin_config,
|
|
66
|
+
columns_config,
|
|
67
|
+
table,
|
|
68
|
+
view_config,
|
|
69
|
+
theme,
|
|
70
|
+
},
|
|
53
71
|
})
|
|
54
72
|
}
|
|
@@ -37,7 +37,7 @@ pub fn get_plugin_config_schema(
|
|
|
37
37
|
renderer: &Renderer,
|
|
38
38
|
view_config: &ViewConfig,
|
|
39
39
|
) -> ApiResult<ColumnConfigSchema> {
|
|
40
|
-
let plugin = renderer.
|
|
40
|
+
let plugin = renderer.ensure_plugin_selected()?;
|
|
41
41
|
let view_config_js =
|
|
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)?;
|
|
@@ -60,7 +60,7 @@ pub fn get_column_config_schema(
|
|
|
60
60
|
current_value: Option<&serde_json::Map<String, serde_json::Value>>,
|
|
61
61
|
abs_max: Option<f64>,
|
|
62
62
|
) -> ApiResult<ColumnConfigSchema> {
|
|
63
|
-
let plugin = renderer.
|
|
63
|
+
let plugin = renderer.ensure_plugin_selected()?;
|
|
64
64
|
let plugin_config = renderer.metadata();
|
|
65
65
|
let names = &plugin_config.config_column_names;
|
|
66
66
|
let group = view_config
|
|
@@ -17,36 +17,62 @@ use wasm_bindgen::JsCast;
|
|
|
17
17
|
use web_sys::*;
|
|
18
18
|
|
|
19
19
|
use crate::js::plugin::JsPerspectiveViewerPlugin;
|
|
20
|
+
use crate::utils::RenderGuard;
|
|
20
21
|
|
|
21
|
-
///
|
|
22
|
-
///
|
|
23
|
-
///
|
|
24
|
-
///
|
|
22
|
+
/// Mount `plugin` (once) under its layout slot in the viewer's light DOM.
|
|
23
|
+
/// Idempotent — a plugin that already has a parent is left untouched (only
|
|
24
|
+
/// its `slot` attribute is refreshed). No style read happens here: the
|
|
25
|
+
/// mount may precede the theme stamp, and the plugin's first `draw()`
|
|
26
|
+
/// captures its `--psp-*` CSS fresh by construction ("stamp before draw").
|
|
25
27
|
///
|
|
26
28
|
/// # Arguments
|
|
27
29
|
/// - `viewer` the root `<perspective-viewer>` element.
|
|
28
30
|
/// - `plugin` the plugin custom element.
|
|
29
|
-
/// - `
|
|
30
|
-
|
|
31
|
+
/// - `slot` the layout slot name (panel id) to mount the plugin under, or
|
|
32
|
+
/// `None` to use the default unnamed `<slot>` (legacy single-panel behavior).
|
|
33
|
+
pub fn mount_plugin(
|
|
31
34
|
viewer: &HtmlElement,
|
|
32
35
|
plugin: &JsPerspectiveViewerPlugin,
|
|
33
|
-
|
|
34
|
-
) -> ApiResult<
|
|
36
|
+
slot: Option<&str>,
|
|
37
|
+
) -> ApiResult<()> {
|
|
35
38
|
let html_plugin = plugin.unchecked_ref::<HtmlElement>();
|
|
39
|
+
if let Some(slot) = slot {
|
|
40
|
+
html_plugin.set_attribute("slot", slot)?;
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
if html_plugin.parent_node().is_none() {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
// TODO(texodus): Place at the bottom of the stack so it is rendered
|
|
45
|
+
// but not visible during a tab switch. This is dumb - fix this with
|
|
46
|
+
// a real life cycle.
|
|
47
|
+
viewer.prepend_with_node_1(html_plugin)?;
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
html_plugin.style().set_property("opacity", "1")?;
|
|
44
|
-
result
|
|
50
|
+
Ok(())
|
|
45
51
|
}
|
|
46
52
|
|
|
53
|
+
/// Given an async `task` which draws `plugin`, [`mount_plugin`] it, then run
|
|
54
|
+
/// `task`. Requires the [`RenderGuard`] lock witness — plugin dispatch
|
|
55
|
+
/// outside a locked run does not compile (invariants I3/I6 of the merge;
|
|
56
|
+
/// see `SESSION_CONFIG_COHERENCE_PLAN.md`).
|
|
57
|
+
pub async fn activate_plugin<T>(
|
|
58
|
+
_guard: &RenderGuard,
|
|
59
|
+
viewer: &HtmlElement,
|
|
60
|
+
plugin: &JsPerspectiveViewerPlugin,
|
|
61
|
+
slot: Option<&str>,
|
|
62
|
+
task: impl Future<Output = ApiResult<T>>,
|
|
63
|
+
) -> ApiResult<T> {
|
|
64
|
+
mount_plugin(viewer, plugin, slot)?;
|
|
65
|
+
task.await
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Remove the previously-active plugin for a panel from the viewer's light DOM.
|
|
69
|
+
///
|
|
70
|
+
/// When `slot` is `Some`, only a plugin mounted under that same slot (panel) is
|
|
71
|
+
/// removed, so the active plugins of *other* panels are left untouched.
|
|
47
72
|
pub fn remove_inactive_plugin(
|
|
48
73
|
viewer: &HtmlElement,
|
|
49
74
|
plugin: &JsPerspectiveViewerPlugin,
|
|
75
|
+
slot: Option<&str>,
|
|
50
76
|
plugins: &[JsPerspectiveViewerPlugin],
|
|
51
77
|
) -> ApiResult<()> {
|
|
52
78
|
for idx in 0..viewer.children().length() {
|
|
@@ -55,6 +81,7 @@ pub fn remove_inactive_plugin(
|
|
|
55
81
|
&& plugins
|
|
56
82
|
.iter()
|
|
57
83
|
.any(|x| *x.unchecked_ref::<Element>() == elem)
|
|
84
|
+
&& (slot.is_none() || elem.get_attribute("slot").as_deref() == slot)
|
|
58
85
|
{
|
|
59
86
|
viewer.remove_child(&elem)?;
|
|
60
87
|
break;
|