@perspective-dev/viewer 5.5.0 → 5.6.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/README.md +82 -3
- package/dist/cdn/perspective-viewer.js +9 -9
- package/dist/cdn/perspective-viewer.js.map +3 -3
- 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 -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/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/column-format.js +2 -0
- package/dist/esm/column-format.js.map +7 -0
- package/dist/esm/extensions.d.ts +3 -13
- package/dist/esm/perspective-viewer.d.ts +1 -1
- package/dist/esm/perspective-viewer.js +9 -9
- package/dist/esm/perspective-viewer.js.map +3 -3
- package/dist/esm/plugin.d.ts +4 -1
- package/dist/esm/select-detail.d.ts +15 -0
- package/dist/esm/select-detail.js +2 -0
- package/dist/esm/select-detail.js.map +7 -0
- package/dist/esm/ts-rs/DerivedTableConfig.d.ts +35 -0
- package/dist/esm/ts-rs/ExportMethod.d.ts +1 -1
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +7 -6
- package/dist/esm/ts-rs/RestoreOptions.d.ts +3 -7
- package/dist/esm/ts-rs/TableRef.d.ts +9 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +7 -6
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -5
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +9 -0
- package/dist/wasm/perspective-viewer.d.ts +126 -35
- package/dist/wasm/perspective-viewer.js +320 -185
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +21 -17
- package/package.json +33 -20
- package/src/css/column-selector.css +205 -50
- package/src/css/ui/containers/context-menu.css +12 -1
- package/src/css/ui/containers/dropdown-menu-items.css +29 -0
- package/src/css/ui/containers/scroll-panel.css +4 -1
- package/src/rust/agent/client/protocol.rs +0 -89
- package/src/rust/agent/markdown/mod.rs +0 -238
- package/src/rust/agent/preamble.md +1 -0
- package/src/rust/agent/tools.rs +2 -16
- package/src/rust/components/column_selector/active_column.rs +15 -5
- package/src/rust/components/column_selector/add_expression_button.rs +9 -26
- package/src/rust/components/column_selector/config_selector.rs +34 -6
- package/src/rust/components/column_selector/derived_control.rs +229 -0
- package/src/rust/components/column_selector/expr_edit_button.rs +3 -2
- package/src/rust/components/column_selector/filter_column.rs +0 -2
- package/src/rust/components/column_selector/inactive_column.rs +4 -5
- package/src/rust/components/column_selector/pivot_column.rs +21 -1
- package/src/rust/components/column_selector/sort_column.rs +4 -3
- package/src/rust/components/column_selector.rs +21 -1
- package/src/rust/components/column_settings_sidebar/save_settings.rs +5 -3
- package/src/rust/components/column_settings_sidebar/style_tab.rs +1 -0
- package/src/rust/components/column_settings_sidebar.rs +25 -19
- package/src/rust/components/debug_panel.rs +38 -29
- package/src/rust/components/dragdrop_list.rs +8 -9
- package/src/rust/components/export_dropdown.rs +6 -0
- package/src/rust/components/main_panel/msg.rs +6 -4
- package/src/rust/components/main_panel/reconcile.rs +6 -1
- package/src/rust/components/main_panel/render.rs +70 -51
- package/src/rust/components/main_panel/update.rs +39 -1
- package/src/rust/components/main_panel.rs +97 -59
- package/src/rust/components/new_panel_menu.rs +69 -1
- package/src/rust/components/panel_menu.rs +63 -12
- package/src/rust/components/plugin_tab.rs +3 -2
- package/src/rust/components/render_warning.rs +2 -1
- package/src/rust/components/settings_panel.rs +4 -4
- package/src/rust/components/status_bar.rs +80 -21
- package/src/rust/components/status_indicator.rs +8 -1
- package/src/rust/components/viewer/filters.rs +15 -3
- package/src/rust/components/viewer/msg.rs +8 -0
- package/src/rust/components/viewer/panels.rs +44 -4
- package/src/rust/components/viewer/render.rs +1 -0
- package/src/rust/components/viewer/settings.rs +0 -22
- package/src/rust/components/viewer/snapshots.rs +2 -2
- package/src/rust/components/viewer/wiring.rs +18 -1
- package/src/rust/components/viewer.rs +47 -27
- package/src/rust/components/window_editor.rs +5 -4
- package/src/rust/config/column_config_schema.rs +38 -377
- package/src/rust/config/export_method.rs +24 -0
- package/src/rust/config/number_string_format.rs +0 -94
- package/src/rust/config/options.rs +3 -7
- package/src/rust/config/plugin_static_config.rs +0 -42
- package/src/rust/config/viewer_config.rs +47 -17
- package/src/rust/config/workspace_config.rs +158 -128
- package/src/rust/custom_elements/viewer.rs +189 -129
- package/src/rust/custom_events.rs +33 -18
- package/src/rust/derived.rs +266 -0
- package/src/rust/exprtk/tokenize.rs +0 -54
- package/src/rust/js/plugin.rs +1 -1
- package/src/rust/lib.rs +3 -0
- package/src/rust/presentation.rs +23 -4
- package/src/rust/queries/column_locator.rs +55 -48
- package/src/rust/queries/get_viewer_config.rs +1 -1
- package/src/rust/queries/plugin_column_styles.rs +22 -23
- package/src/rust/renderer/dispatch.rs +2 -0
- package/src/rust/renderer/plugin_config.rs +652 -487
- package/src/rust/renderer/props.rs +4 -0
- package/src/rust/renderer/registry.rs +2 -6
- package/src/rust/renderer/state.rs +205 -0
- package/src/rust/renderer.rs +201 -59
- package/src/rust/session/metadata.rs +35 -19
- package/src/rust/session/op_queue.rs +430 -0
- package/src/rust/session/panel_state.rs +327 -0
- package/src/rust/session/view_subscription.rs +42 -3
- package/src/rust/session.rs +931 -419
- package/src/rust/tasks/apply_global_filters.rs +49 -35
- package/src/rust/tasks/auto_pause.rs +22 -12
- package/src/rust/tasks/copy_export.rs +72 -0
- package/src/rust/tasks/create_panel.rs +46 -21
- package/src/rust/tasks/derive.rs +312 -0
- package/src/rust/tasks/mod.rs +6 -2
- package/src/rust/tasks/pipeline.rs +152 -89
- package/src/rust/tasks/reset_all.rs +31 -20
- package/src/rust/tasks/restore_derived.rs +200 -0
- package/src/rust/tasks/restore_panel.rs +153 -141
- package/src/rust/tasks/send_column_config.rs +56 -29
- package/src/rust/tasks/send_plugin_config.rs +52 -23
- package/src/rust/tasks/set_edit_mode.rs +33 -25
- package/src/rust/tasks/sync_update_panels.rs +40 -13
- package/src/rust/tasks/table_lifecycle.rs +70 -34
- package/src/rust/tasks/transactional_restore.rs +387 -0
- package/src/rust/tasks/update_theme.rs +45 -6
- package/src/rust/ui/containers/scroll_panel.rs +17 -10
- package/src/rust/ui/containers/split_panel.rs +0 -41
- package/src/rust/ui/context_menu.rs +157 -18
- package/src/rust/ui/font_loader.rs +23 -35
- package/src/rust/ui/form/mirrored_textarea.rs +0 -12
- package/src/rust/ui/form/multi_stop_gradient_selector.rs +0 -89
- package/src/rust/ui/form/palette_selector.rs +0 -51
- package/src/rust/ui/portal.rs +4 -10
- package/src/rust/utils/browser/mod.rs +2 -3
- package/src/{ts/perspective-viewer.inline.ts → rust/utils/browser/mouse.rs} +11 -10
- package/src/rust/utils/browser/request_animation_frame.rs +27 -8
- package/src/rust/utils/css.rs +0 -360
- package/src/rust/utils/{tests/mod.rs → in_flight.rs} +62 -2
- package/src/rust/utils/local_fonts.rs +0 -36
- package/src/rust/utils/mod.rs +4 -3
- package/src/rust/utils/modal_position.rs +15 -21
- package/src/rust/utils/pending_effects.rs +6 -30
- package/src/rust/utils/{browser/tests/mod.rs → plugin_host_state.rs} +31 -1
- package/src/rust/utils/pubsub.rs +0 -1
- package/src/rust/workspace.rs +187 -140
- package/src/svg/derived.svg +6 -0
- package/src/themes/icons.css +1 -0
- package/src/themes/intl/ar.css +10 -0
- package/src/themes/intl/de.css +10 -0
- package/src/themes/intl/es.css +10 -0
- package/src/themes/intl/fr.css +10 -0
- package/src/themes/intl/ja.css +10 -0
- package/src/themes/intl/pt.css +10 -0
- package/src/themes/intl/zh.css +10 -0
- package/src/themes/intl.css +10 -0
- package/src/themes/monokai.css +1 -1
- package/src/themes/vaporwave.css +1 -1
- package/src/ts/extensions.ts +3 -36
- package/src/ts/perspective-viewer.ts +1 -1
- package/src/ts/plugin.ts +4 -1
- package/src/{rust/utils/tests/request_animation_frame.rs → ts/select-detail.ts} +32 -26
- package/src/ts/ts-rs/DerivedTableConfig.ts +22 -0
- package/src/ts/ts-rs/ExportMethod.ts +1 -1
- package/src/ts/ts-rs/PanelViewerConfig.ts +7 -6
- package/src/ts/ts-rs/RestoreOptions.ts +3 -7
- package/src/ts/ts-rs/TableRef.ts +9 -0
- package/src/ts/ts-rs/ViewerConfig.ts +7 -6
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +7 -5
- package/src/ts/ts-rs/WorkspaceConfig.ts +7 -1
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +7 -0
- package/dist/esm/perspective-viewer.inline.d.ts +0 -3
- package/dist/esm/perspective-viewer.inline.js +0 -21
- package/dist/esm/perspective-viewer.inline.js.map +0 -7
- package/src/rust/tasks/restore_and_render.rs +0 -107
- package/src/rust/utils/browser/tests/debounce.rs +0 -163
- package/src/rust/utils/tests/pubsub.rs +0 -95
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline5.js +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
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 perspective_client::config::{ColumnType, Filter, ViewConfig};
|
|
16
|
+
use perspective_client::{Client, Description, Table};
|
|
17
|
+
|
|
18
|
+
use super::TableErrorState;
|
|
19
|
+
use super::metadata::SessionMetadata;
|
|
20
|
+
|
|
21
|
+
/// One panel's committed state, as ONE immutable value: a holder of an
|
|
22
|
+
/// `Rc<PanelState>` sees a consistent snapshot forever, and a change is a new
|
|
23
|
+
/// value swapped in whole.
|
|
24
|
+
#[derive(Clone, Default)]
|
|
25
|
+
pub struct PanelState {
|
|
26
|
+
pub chrome: Chrome,
|
|
27
|
+
pub binding: Binding,
|
|
28
|
+
|
|
29
|
+
/// The element's global filter as broadcast to this panel — the WHOLE set.
|
|
30
|
+
pub overlay: Rc<Vec<OverlayClause>>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// One clause of the element-level global filter, with the type its column has
|
|
34
|
+
/// in the panel that BROADCAST it.
|
|
35
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
36
|
+
pub struct OverlayClause {
|
|
37
|
+
pub filter: Filter,
|
|
38
|
+
pub column_type: Option<ColumnType>,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
impl From<Filter> for OverlayClause {
|
|
42
|
+
/// A clause with no broadcaster.
|
|
43
|
+
fn from(filter: Filter) -> Self {
|
|
44
|
+
Self {
|
|
45
|
+
filter,
|
|
46
|
+
column_type: None,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// The config a `View` is built from: `config`, plus each `overlay` clause the
|
|
52
|
+
/// listening table can honor — one naming a column the listener has, with the
|
|
53
|
+
/// broadcaster's type.
|
|
54
|
+
pub fn effective(
|
|
55
|
+
config: &ViewConfig,
|
|
56
|
+
overlay: &[OverlayClause],
|
|
57
|
+
type_of: &dyn Fn(&str) -> Option<ColumnType>,
|
|
58
|
+
) -> (ViewConfig, Vec<usize>) {
|
|
59
|
+
let mut effective = config.clone();
|
|
60
|
+
let mut skipped = Vec::new();
|
|
61
|
+
for (idx, clause) in overlay.iter().enumerate() {
|
|
62
|
+
let applies = match (type_of(clause.filter.column()), clause.column_type) {
|
|
63
|
+
(Some(listener), Some(broadcaster)) => listener == broadcaster,
|
|
64
|
+
(Some(_), None) => true,
|
|
65
|
+
(None, _) => false,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if applies {
|
|
69
|
+
effective.filter.push(clause.filter.clone());
|
|
70
|
+
} else {
|
|
71
|
+
skipped.push(idx);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
(effective, skipped)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Panel presentation that is not a function of the data.
|
|
79
|
+
#[derive(Clone, Default)]
|
|
80
|
+
pub struct Chrome {
|
|
81
|
+
pub title: Option<String>,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// What this panel is bound to.
|
|
85
|
+
#[derive(Clone)]
|
|
86
|
+
pub enum Binding {
|
|
87
|
+
/// No table requested.
|
|
88
|
+
Unbound {
|
|
89
|
+
client: Option<Client>,
|
|
90
|
+
config: Rc<ViewConfig>,
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/// A named table that no client hosts yet; `client` is the one asked.
|
|
94
|
+
Awaiting {
|
|
95
|
+
client: Client,
|
|
96
|
+
name: String,
|
|
97
|
+
config: Rc<ViewConfig>,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
/// Bound to `table`.
|
|
101
|
+
Bound(Rc<BoundState>),
|
|
102
|
+
|
|
103
|
+
/// The binding failed or its connection dropped.
|
|
104
|
+
Lost {
|
|
105
|
+
prior: Rc<Binding>,
|
|
106
|
+
error: TableErrorState,
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
impl Default for Binding {
|
|
111
|
+
fn default() -> Self {
|
|
112
|
+
Self::Unbound {
|
|
113
|
+
client: None,
|
|
114
|
+
config: Rc::default(),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[derive(Clone)]
|
|
120
|
+
pub struct BoundState {
|
|
121
|
+
pub table: Table,
|
|
122
|
+
pub metadata: Rc<SessionMetadata>,
|
|
123
|
+
pub config: Rc<ViewConfig>,
|
|
124
|
+
|
|
125
|
+
/// The server's [`Description`] of the last validated effective config,
|
|
126
|
+
/// keyed by that config.
|
|
127
|
+
pub description: Option<(Rc<ViewConfig>, Rc<Description>)>,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
impl Binding {
|
|
131
|
+
/// This binding, seen through a [`Binding::Lost`] to what it was.
|
|
132
|
+
fn live(&self) -> &Binding {
|
|
133
|
+
match self {
|
|
134
|
+
Binding::Lost { prior, .. } => prior,
|
|
135
|
+
binding => binding,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// `f` applied to the live binding, preserving a `Lost` wrapper.
|
|
140
|
+
fn map_live(&self, f: impl FnOnce(&Binding) -> Binding) -> Binding {
|
|
141
|
+
match self {
|
|
142
|
+
Binding::Lost { prior, error } => Binding::Lost {
|
|
143
|
+
prior: Rc::new(f(prior)),
|
|
144
|
+
error: error.clone(),
|
|
145
|
+
},
|
|
146
|
+
binding => f(binding),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
impl PanelState {
|
|
152
|
+
pub fn config(&self) -> &Rc<ViewConfig> {
|
|
153
|
+
match self.binding.live() {
|
|
154
|
+
Binding::Unbound { config, .. } | Binding::Awaiting { config, .. } => config,
|
|
155
|
+
Binding::Bound(bound) => &bound.config,
|
|
156
|
+
Binding::Lost { .. } => unreachable!("`Lost::prior` is never `Lost`"),
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/// The client this panel is bound through, awaits a table on, or will look
|
|
161
|
+
/// one up on.
|
|
162
|
+
pub fn client(&self) -> Option<Client> {
|
|
163
|
+
match self.binding.live() {
|
|
164
|
+
Binding::Unbound { client, .. } => client.clone(),
|
|
165
|
+
Binding::Awaiting { client, .. } => Some(client.clone()),
|
|
166
|
+
Binding::Bound(bound) => Some(bound.table.get_client()),
|
|
167
|
+
Binding::Lost { .. } => unreachable!("`Lost::prior` is never `Lost`"),
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
pub fn bound(&self) -> Option<&Rc<BoundState>> {
|
|
172
|
+
match self.binding.live() {
|
|
173
|
+
Binding::Bound(bound) => Some(bound),
|
|
174
|
+
_ => None,
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
pub fn awaiting(&self) -> Option<&str> {
|
|
179
|
+
match self.binding.live() {
|
|
180
|
+
Binding::Awaiting { name, .. } => Some(name),
|
|
181
|
+
_ => None,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub fn lost(&self) -> Option<&TableErrorState> {
|
|
186
|
+
match &self.binding {
|
|
187
|
+
Binding::Lost { error, .. } => Some(error),
|
|
188
|
+
_ => None,
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// This state with its binding lost to `error`.
|
|
193
|
+
pub fn with_lost(&self, error: TableErrorState) -> Self {
|
|
194
|
+
Self {
|
|
195
|
+
binding: Binding::Lost {
|
|
196
|
+
prior: Rc::new(self.binding.live().clone()),
|
|
197
|
+
error,
|
|
198
|
+
},
|
|
199
|
+
..self.clone()
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/// This state with a lost binding recovered to what it was.
|
|
204
|
+
pub fn recovered(&self) -> Self {
|
|
205
|
+
Self {
|
|
206
|
+
binding: self.binding.live().clone(),
|
|
207
|
+
..self.clone()
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/// This state with `config` in place of its view config.
|
|
212
|
+
pub fn with_config(&self, config: Rc<ViewConfig>) -> Self {
|
|
213
|
+
let binding = self.binding.map_live(|binding| match binding {
|
|
214
|
+
Binding::Unbound { client, .. } => Binding::Unbound {
|
|
215
|
+
client: client.clone(),
|
|
216
|
+
config,
|
|
217
|
+
},
|
|
218
|
+
Binding::Awaiting { client, name, .. } => Binding::Awaiting {
|
|
219
|
+
client: client.clone(),
|
|
220
|
+
name: name.clone(),
|
|
221
|
+
config,
|
|
222
|
+
},
|
|
223
|
+
Binding::Bound(bound) => Binding::Bound(Rc::new(BoundState {
|
|
224
|
+
config,
|
|
225
|
+
description: None,
|
|
226
|
+
..(**bound).clone()
|
|
227
|
+
})),
|
|
228
|
+
Binding::Lost { .. } => unreachable!("`Lost::prior` is never `Lost`"),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
Self {
|
|
232
|
+
binding,
|
|
233
|
+
..self.clone()
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// This state with nothing bound and nothing awaited (a lost binding is
|
|
238
|
+
/// replaced, not recovered); the client and config are kept.
|
|
239
|
+
pub fn unbound(&self) -> Self {
|
|
240
|
+
self.unbound_on(self.client())
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// This state with nothing bound, looking tables up on `client`.
|
|
244
|
+
pub fn unbound_on(&self, client: Option<Client>) -> Self {
|
|
245
|
+
Self {
|
|
246
|
+
binding: Binding::Unbound {
|
|
247
|
+
client,
|
|
248
|
+
config: self.config().clone(),
|
|
249
|
+
},
|
|
250
|
+
..self.clone()
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/// This state awaiting a host for `name` on `client`; the config is kept.
|
|
255
|
+
pub fn awaiting_table(&self, client: Client, name: String) -> Self {
|
|
256
|
+
Self {
|
|
257
|
+
binding: Binding::Awaiting {
|
|
258
|
+
client,
|
|
259
|
+
name,
|
|
260
|
+
config: self.config().clone(),
|
|
261
|
+
},
|
|
262
|
+
..self.clone()
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/// This state with `overlay` broadcast to it.
|
|
267
|
+
pub fn with_overlay(&self, overlay: Rc<Vec<OverlayClause>>) -> Self {
|
|
268
|
+
Self {
|
|
269
|
+
overlay,
|
|
270
|
+
..self.with_description(None)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/// This state bound to `table`; the config is kept and nothing has been
|
|
275
|
+
/// described against the new table yet.
|
|
276
|
+
pub fn bound_to(&self, table: Table, metadata: SessionMetadata) -> Self {
|
|
277
|
+
Self {
|
|
278
|
+
binding: Binding::Bound(Rc::new(BoundState {
|
|
279
|
+
table,
|
|
280
|
+
metadata: Rc::new(metadata),
|
|
281
|
+
config: self.config().clone(),
|
|
282
|
+
description: None,
|
|
283
|
+
})),
|
|
284
|
+
..self.clone()
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// This state with `metadata` in place of its table metadata; a no-op
|
|
289
|
+
/// unless bound.
|
|
290
|
+
pub fn with_metadata(&self, metadata: Rc<SessionMetadata>) -> Self {
|
|
291
|
+
let binding = self.binding.map_live(|binding| match binding {
|
|
292
|
+
Binding::Bound(bound) => Binding::Bound(Rc::new(BoundState {
|
|
293
|
+
metadata,
|
|
294
|
+
..(**bound).clone()
|
|
295
|
+
})),
|
|
296
|
+
binding => binding.clone(),
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
Self {
|
|
300
|
+
binding,
|
|
301
|
+
..self.clone()
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/// This state with `description` recorded; a no-op unless bound.
|
|
306
|
+
pub fn with_description(&self, description: Option<(Rc<ViewConfig>, Rc<Description>)>) -> Self {
|
|
307
|
+
let binding = self.binding.map_live(|binding| match binding {
|
|
308
|
+
Binding::Bound(bound) => Binding::Bound(Rc::new(BoundState {
|
|
309
|
+
description,
|
|
310
|
+
..(**bound).clone()
|
|
311
|
+
})),
|
|
312
|
+
binding => binding.clone(),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
Self {
|
|
316
|
+
binding,
|
|
317
|
+
..self.clone()
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
pub fn with_title(&self, title: Option<String>) -> Self {
|
|
322
|
+
Self {
|
|
323
|
+
chrome: Chrome { title },
|
|
324
|
+
..self.clone()
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
@@ -43,6 +43,11 @@ struct ViewSubscriptionData {
|
|
|
43
43
|
on_update: Option<Callback<()>>,
|
|
44
44
|
is_deleted: Rc<Cell<bool>>,
|
|
45
45
|
|
|
46
|
+
/// Set by [`ViewSubscription::quiesce`]: the engine registration is gone
|
|
47
|
+
/// and BOTH emit channels are dead, including any update already in
|
|
48
|
+
/// flight when the flag flipped.
|
|
49
|
+
quiesced: Rc<Cell<bool>>,
|
|
50
|
+
|
|
46
51
|
/// The bound `View`'s latest known dimensions, written before
|
|
47
52
|
/// `on_update`/`on_stats` fire.
|
|
48
53
|
dimensions: Rc<RefCell<Option<ViewDimensionsResp>>>,
|
|
@@ -57,8 +62,14 @@ pub struct ViewSubscription {
|
|
|
57
62
|
impl ViewSubscriptionData {
|
|
58
63
|
/// Main handler when underlying `View()` calls `on_update()`.
|
|
59
64
|
async fn on_view_update(self) -> ApiResult<JsValue> {
|
|
65
|
+
if self.quiesced.get() {
|
|
66
|
+
return Ok(JsValue::UNDEFINED);
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
self.clone().update_view_stats().await?;
|
|
61
|
-
if let Some(on_update) = &self.on_update
|
|
70
|
+
if let Some(on_update) = &self.on_update
|
|
71
|
+
&& !self.quiesced.get()
|
|
72
|
+
{
|
|
62
73
|
on_update.emit(());
|
|
63
74
|
};
|
|
64
75
|
|
|
@@ -66,6 +77,10 @@ impl ViewSubscriptionData {
|
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
async fn update_view_stats(self) -> ApiResult<JsValue> {
|
|
80
|
+
if self.quiesced.get() {
|
|
81
|
+
return Ok(JsValue::UNDEFINED);
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
let dimensions = self.view.dimensions().await?;
|
|
70
85
|
let num_rows = dimensions.num_table_rows;
|
|
71
86
|
let num_cols = dimensions.num_table_columns;
|
|
@@ -80,7 +95,10 @@ impl ViewSubscriptionData {
|
|
|
80
95
|
is_split_by: !self.config.split_by.is_empty(),
|
|
81
96
|
};
|
|
82
97
|
|
|
83
|
-
self.
|
|
98
|
+
if !self.quiesced.get() {
|
|
99
|
+
self.on_stats.emit(stats);
|
|
100
|
+
}
|
|
101
|
+
|
|
84
102
|
Ok(JsValue::UNDEFINED)
|
|
85
103
|
}
|
|
86
104
|
|
|
@@ -90,7 +108,7 @@ impl ViewSubscriptionData {
|
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
let view = &self.view;
|
|
93
|
-
if self.on_update.is_some() {
|
|
111
|
+
if self.on_update.is_some() && !self.quiesced.get() {
|
|
94
112
|
view.remove_update(self.callback_id.get()).await?;
|
|
95
113
|
}
|
|
96
114
|
|
|
@@ -125,6 +143,7 @@ impl ViewSubscription {
|
|
|
125
143
|
callback_id: Rc::default(),
|
|
126
144
|
on_update,
|
|
127
145
|
is_deleted: Rc::default(),
|
|
146
|
+
quiesced: Rc::default(),
|
|
128
147
|
dimensions: Rc::default(),
|
|
129
148
|
};
|
|
130
149
|
|
|
@@ -191,6 +210,26 @@ impl ViewSubscription {
|
|
|
191
210
|
self.data.build_config.clone()
|
|
192
211
|
}
|
|
193
212
|
|
|
213
|
+
/// Permanently detach this subscription from its `Session` — the engine
|
|
214
|
+
/// `on_update` registration removed and both emit channels silenced
|
|
215
|
+
/// (in-flight updates included) — leaving a bare keep-alive handle for a
|
|
216
|
+
/// `View` that now feeds a derived `Table` rather than this panel.
|
|
217
|
+
pub async fn quiesce(&self) -> ApiResult<()> {
|
|
218
|
+
if self.data.quiesced.get() {
|
|
219
|
+
return Ok(());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if self.data.on_update.is_some() {
|
|
223
|
+
self.data
|
|
224
|
+
.view
|
|
225
|
+
.remove_update(self.data.callback_id.get())
|
|
226
|
+
.await?;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
self.data.quiesced.set(true);
|
|
230
|
+
Ok(())
|
|
231
|
+
}
|
|
232
|
+
|
|
194
233
|
/// Delete this `View`. Neglecting to call this method before a
|
|
195
234
|
/// `ViewSubscription` is dropped will result in a log warning, but the
|
|
196
235
|
/// `View` will not leak.
|