@perspective-dev/viewer 4.1.1 → 4.3.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 +2 -2
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/botanical.css +1 -0
- 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/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/esm/extensions.d.ts +32 -1
- package/dist/esm/perspective-viewer.d.ts +1 -0
- package/dist/esm/perspective-viewer.inline.js +2 -2
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +2 -2
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/ts-rs/GroupRollupMode.d.ts +1 -0
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +2 -0
- package/dist/wasm/perspective-viewer.d.ts +1403 -1319
- package/dist/wasm/perspective-viewer.js +3531 -3268
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +111 -99
- package/package.json +4 -3
- package/src/less/column-selector.less +2 -2
- package/src/less/config-selector.less +66 -4
- package/src/rust/components/column_dropdown.rs +8 -8
- package/src/rust/components/column_selector/config_selector.rs +102 -29
- package/src/rust/components/column_selector/filter_column.rs +12 -12
- package/src/rust/components/column_selector/pivot_column.rs +12 -7
- package/src/rust/components/column_selector.rs +27 -17
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +5 -5
- package/src/rust/components/column_settings_sidebar.rs +2 -4
- package/src/rust/components/containers/dragdrop_list.rs +32 -10
- package/src/rust/components/containers/scroll_panel.rs +8 -1
- package/src/rust/components/containers/select.rs +9 -9
- package/src/rust/components/containers/split_panel.rs +2 -2
- package/src/rust/components/filter_dropdown.rs +8 -8
- package/src/rust/components/function_dropdown.rs +8 -8
- package/src/rust/components/modal.rs +4 -4
- package/src/rust/components/plugin_selector.rs +15 -5
- package/src/rust/components/status_indicator.rs +3 -0
- package/src/rust/custom_elements/filter_dropdown.rs +4 -4
- package/src/rust/custom_elements/viewer.rs +2 -7
- package/src/rust/js/plugin.rs +19 -0
- package/src/rust/model/copy_export.rs +2 -1
- package/src/rust/model/intersection_observer.rs +3 -1
- package/src/rust/presentation/sheets.rs +4 -1
- package/src/rust/presentation.rs +4 -4
- package/src/rust/renderer/registry.rs +8 -1
- package/src/rust/session/column_defaults_update.rs +18 -0
- package/src/rust/session/replace_expression_update.rs +1 -0
- package/src/themes/botanical.less +142 -0
- package/src/themes/themes.less +2 -1
- package/src/ts/extensions.ts +73 -2
- package/src/ts/perspective-viewer.ts +1 -0
- package/src/ts/ts-rs/GroupRollupMode.ts +3 -0
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -1
- package/tsconfig.json +1 -0
- /package/dist/wasm/snippets/{perspective-viewer-11a3c51b6310ee99 → perspective-viewer-d729f682ba5c19df}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-11a3c51b6310ee99/inline2.js → perspective-viewer-d729f682ba5c19df/inline1.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-11a3c51b6310ee99/inline1.js → perspective-viewer-d729f682ba5c19df/inline2.js} +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-11a3c51b6310ee99 → perspective-viewer-d729f682ba5c19df}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-11a3c51b6310ee99 → perspective-viewer-d729f682ba5c19df}/inline4.js +0 -0
|
@@ -514,18 +514,18 @@ impl FilterColumnProps {
|
|
|
514
514
|
}
|
|
515
515
|
};
|
|
516
516
|
|
|
517
|
-
if let Some(input) = filter_input
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
517
|
+
if let Some(input) = filter_input
|
|
518
|
+
&& &input != filter_column.term()
|
|
519
|
+
{
|
|
520
|
+
*filter_column.term_mut() = input;
|
|
521
|
+
let update = ViewConfigUpdate {
|
|
522
|
+
filter: Some(filters),
|
|
523
|
+
..ViewConfigUpdate::default()
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
self.update_and_render(update)
|
|
527
|
+
.map(ApiFuture::spawn)
|
|
528
|
+
.unwrap_or_log();
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
531
|
}
|
|
@@ -26,11 +26,15 @@ pub struct PivotColumnProps {
|
|
|
26
26
|
/// Column name.
|
|
27
27
|
pub column: String,
|
|
28
28
|
|
|
29
|
+
#[prop_or_default]
|
|
30
|
+
pub column_type: Option<ColumnType>,
|
|
31
|
+
|
|
29
32
|
/// The drag starte of this column, if applicable.
|
|
30
33
|
pub action: DragTarget,
|
|
31
34
|
|
|
32
35
|
// State
|
|
33
|
-
|
|
36
|
+
#[prop_or_default]
|
|
37
|
+
pub opt_session: Option<Session>,
|
|
34
38
|
pub dragdrop: DragDrop,
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -74,12 +78,13 @@ impl Component for PivotColumn {
|
|
|
74
78
|
move |_event| dragdrop.notify_drag_end()
|
|
75
79
|
});
|
|
76
80
|
|
|
77
|
-
let col_type = ctx
|
|
78
|
-
.props()
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
let col_type = ctx.props().column_type.unwrap_or_else(|| {
|
|
82
|
+
ctx.props()
|
|
83
|
+
.opt_session
|
|
84
|
+
.as_ref()
|
|
85
|
+
.and_then(|x| x.metadata().get_column_table_type(&ctx.props().column))
|
|
86
|
+
.unwrap_or(ColumnType::Integer)
|
|
87
|
+
});
|
|
83
88
|
|
|
84
89
|
html! {
|
|
85
90
|
<div
|
|
@@ -28,6 +28,7 @@ use std::rc::Rc;
|
|
|
28
28
|
pub use empty_column::*;
|
|
29
29
|
pub use invalid_column::*;
|
|
30
30
|
use perspective_js::utils::ApiFuture;
|
|
31
|
+
pub use pivot_column::*;
|
|
31
32
|
use web_sys::*;
|
|
32
33
|
use yew::prelude::*;
|
|
33
34
|
|
|
@@ -77,6 +78,7 @@ pub enum ColumnSelectorMsg {
|
|
|
77
78
|
TableLoaded,
|
|
78
79
|
ViewCreated,
|
|
79
80
|
HoverActiveIndex(Option<usize>),
|
|
81
|
+
SetWidth(f64),
|
|
80
82
|
Drag(DragEffect),
|
|
81
83
|
DragEnd,
|
|
82
84
|
Drop((String, DragTarget, DragEffect, usize)),
|
|
@@ -91,6 +93,7 @@ pub struct ColumnSelector {
|
|
|
91
93
|
named_row_count: usize,
|
|
92
94
|
drag_container: DragDropContainer,
|
|
93
95
|
column_dropdown: ColumnDropDownElement,
|
|
96
|
+
viewport_width: f64,
|
|
94
97
|
on_reset: Rc<PubSub<()>>,
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -147,6 +150,7 @@ impl Component for ColumnSelector {
|
|
|
147
150
|
Self {
|
|
148
151
|
_subscriptions: [table_sub, view_sub, drop_sub, drag_sub, dragend_sub],
|
|
149
152
|
named_row_count,
|
|
153
|
+
viewport_width: 0f64,
|
|
150
154
|
drag_container,
|
|
151
155
|
column_dropdown,
|
|
152
156
|
on_reset: Default::default(),
|
|
@@ -157,6 +161,10 @@ impl Component for ColumnSelector {
|
|
|
157
161
|
match msg {
|
|
158
162
|
Drag(DragEffect::Move(DragTarget::Active)) => false,
|
|
159
163
|
Drag(_) | DragEnd | TableLoaded => true,
|
|
164
|
+
SetWidth(w) => {
|
|
165
|
+
self.viewport_width = w;
|
|
166
|
+
false
|
|
167
|
+
},
|
|
160
168
|
ViewCreated => {
|
|
161
169
|
let named = maybe! {
|
|
162
170
|
let plugin =
|
|
@@ -355,11 +363,7 @@ impl Component for ColumnSelector {
|
|
|
355
363
|
})
|
|
356
364
|
.collect();
|
|
357
365
|
|
|
358
|
-
let size =
|
|
359
|
-
56.0
|
|
360
|
-
} else {
|
|
361
|
-
28.0
|
|
362
|
-
};
|
|
366
|
+
let size = 28.0;
|
|
363
367
|
|
|
364
368
|
let add_column = if ctx
|
|
365
369
|
.props()
|
|
@@ -389,8 +393,8 @@ impl Component for ColumnSelector {
|
|
|
389
393
|
inactive_children.insert(0, add_column);
|
|
390
394
|
}
|
|
391
395
|
|
|
392
|
-
let selected_columns = html! {
|
|
393
|
-
<div id="selected-columns">
|
|
396
|
+
let mut selected_columns = vec![html! {
|
|
397
|
+
<div id="selected-columns" key="__active_columns__">
|
|
394
398
|
<ScrollPanel
|
|
395
399
|
id="active-columns"
|
|
396
400
|
class={active_classes}
|
|
@@ -398,13 +402,27 @@ impl Component for ColumnSelector {
|
|
|
398
402
|
dragenter={&self.drag_container.dragenter}
|
|
399
403
|
dragleave={&self.drag_container.dragleave}
|
|
400
404
|
viewport_ref={&self.drag_container.noderef}
|
|
405
|
+
initial_width={self.viewport_width}
|
|
406
|
+
on_auto_width={ctx.link().callback(ColumnSelectorMsg::SetWidth)}
|
|
401
407
|
drop={ondrop}
|
|
402
408
|
on_resize={&ctx.props().on_resize}
|
|
403
409
|
on_dimensions_reset={&self.on_reset}
|
|
404
410
|
children={std::iter::once(config_selector).chain(active_columns).collect::<Vec<_>>()}
|
|
405
411
|
/>
|
|
406
412
|
</div>
|
|
407
|
-
};
|
|
413
|
+
}];
|
|
414
|
+
|
|
415
|
+
if !inactive_children.is_empty() {
|
|
416
|
+
selected_columns.push(html! {
|
|
417
|
+
<ScrollPanel
|
|
418
|
+
id="sub-columns"
|
|
419
|
+
key="__sub_columns__"
|
|
420
|
+
on_resize={&ctx.props().on_resize}
|
|
421
|
+
on_dimensions_reset={&self.on_reset}
|
|
422
|
+
children={inactive_children}
|
|
423
|
+
/>
|
|
424
|
+
})
|
|
425
|
+
}
|
|
408
426
|
|
|
409
427
|
html! {
|
|
410
428
|
<>
|
|
@@ -415,15 +433,7 @@ impl Component for ColumnSelector {
|
|
|
415
433
|
skip_empty=true
|
|
416
434
|
orientation={Orientation::Vertical}
|
|
417
435
|
>
|
|
418
|
-
{ selected_columns }
|
|
419
|
-
if !inactive_children.is_empty() {
|
|
420
|
-
<ScrollPanel
|
|
421
|
-
id="sub-columns"
|
|
422
|
-
on_resize={&ctx.props().on_resize}
|
|
423
|
-
on_dimensions_reset={&self.on_reset}
|
|
424
|
-
children={inactive_children}
|
|
425
|
-
/>
|
|
426
|
-
}
|
|
436
|
+
{ for selected_columns }
|
|
427
437
|
</SplitPanel>
|
|
428
438
|
</>
|
|
429
439
|
}
|
|
@@ -55,15 +55,15 @@ pub fn row_selector(props: &RowSelectorProps) -> Html {
|
|
|
55
55
|
|
|
56
56
|
let inner = if props.selected_row.is_none() || props.focused {
|
|
57
57
|
let mut pairs = props.pairs.clone();
|
|
58
|
-
if let Some(ref rowval) = props.selected_row
|
|
59
|
-
|
|
58
|
+
if let Some(ref rowval) = props.selected_row
|
|
59
|
+
&& let Some((i, _)) = pairs.iter().find_position(|pair| {
|
|
60
60
|
pair.key
|
|
61
61
|
.as_ref()
|
|
62
62
|
.map(|keyval| keyval == rowval)
|
|
63
63
|
.unwrap_or_default()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
})
|
|
65
|
+
{
|
|
66
|
+
pairs.remove(i);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
let exclude: HashSet<_> = pairs
|
|
@@ -156,7 +156,7 @@ impl Component for ColumnSettingsPanel {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
fn update(&mut self, ctx: &yew::prelude::Context<Self>, msg: Self::Message) -> bool {
|
|
159
|
-
|
|
159
|
+
match msg {
|
|
160
160
|
ColumnSettingsPanelMsg::SetExprValue(val) => {
|
|
161
161
|
if self.expr_value != val {
|
|
162
162
|
self.expr_value = val;
|
|
@@ -241,9 +241,7 @@ impl Component for ColumnSettingsPanel {
|
|
|
241
241
|
false
|
|
242
242
|
}
|
|
243
243
|
},
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
result
|
|
244
|
+
}
|
|
247
245
|
}
|
|
248
246
|
|
|
249
247
|
fn view(&self, ctx: &yew::prelude::Context<Self>) -> Html {
|
|
@@ -14,11 +14,13 @@ use std::collections::HashSet;
|
|
|
14
14
|
use std::marker::PhantomData;
|
|
15
15
|
|
|
16
16
|
use derivative::Derivative;
|
|
17
|
+
use perspective_client::proto::ColumnType;
|
|
17
18
|
use web_sys::*;
|
|
18
19
|
use yew::html::Scope;
|
|
19
20
|
use yew::prelude::*;
|
|
20
21
|
|
|
21
22
|
use crate::components::column_selector::{EmptyColumn, InPlaceColumn, InvalidColumn};
|
|
23
|
+
use crate::components::type_icon::TypeIcon;
|
|
22
24
|
use crate::custom_elements::ColumnDropDownElement;
|
|
23
25
|
use crate::dragdrop::*;
|
|
24
26
|
use crate::utils::DragTarget;
|
|
@@ -32,12 +34,16 @@ where
|
|
|
32
34
|
<U as Component>::Properties: DragDropListItemProps,
|
|
33
35
|
{
|
|
34
36
|
pub parent: Scope<T>,
|
|
37
|
+
|
|
35
38
|
pub dragdrop: DragDrop,
|
|
36
39
|
pub name: &'static str,
|
|
37
40
|
pub column_dropdown: ColumnDropDownElement,
|
|
38
41
|
pub exclude: HashSet<String>,
|
|
39
42
|
pub children: ChildrenWithProps<U>,
|
|
40
43
|
|
|
44
|
+
#[prop_or_default]
|
|
45
|
+
pub disabled: bool,
|
|
46
|
+
|
|
41
47
|
#[prop_or_default]
|
|
42
48
|
pub is_dragover: Option<(
|
|
43
49
|
usize,
|
|
@@ -59,6 +65,7 @@ where
|
|
|
59
65
|
&& self.children == other.children
|
|
60
66
|
&& self.allow_duplicates == other.allow_duplicates
|
|
61
67
|
&& self.is_dragover == other.is_dragover
|
|
68
|
+
&& self.disabled == other.disabled
|
|
62
69
|
}
|
|
63
70
|
}
|
|
64
71
|
|
|
@@ -213,10 +220,11 @@ where
|
|
|
213
220
|
.position(|x| x.1.1.as_ref().unwrap().props.get_item() == *column);
|
|
214
221
|
|
|
215
222
|
valid_duplicate_drag = is_duplicate.is_some() && !ctx.props().allow_duplicates;
|
|
216
|
-
if let Some(duplicate) = is_duplicate
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
if let Some(duplicate) = is_duplicate
|
|
224
|
+
&& !is_append
|
|
225
|
+
&& (!ctx.props().allow_duplicates || is_self_move)
|
|
226
|
+
{
|
|
227
|
+
columns.remove(duplicate);
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
// If inserting into the middle of the list, use
|
|
@@ -282,20 +290,34 @@ where
|
|
|
282
290
|
let column_dropdown = ctx.props().column_dropdown.clone();
|
|
283
291
|
let exclude = ctx.props().exclude.clone();
|
|
284
292
|
let on_select = ctx.props().parent.callback(V::create);
|
|
293
|
+
let class = classes!("rrow");
|
|
294
|
+
let is_enabled = true;
|
|
295
|
+
|
|
285
296
|
html! {
|
|
286
|
-
<div ref={&self.elem} class
|
|
297
|
+
<div ref={&self.elem} {class}>
|
|
287
298
|
<div
|
|
288
299
|
id={ctx.props().name}
|
|
289
|
-
ondragover={dragover}
|
|
290
|
-
ondragenter={drag_container.dragenter}
|
|
291
|
-
ondragleave={drag_container.dragleave}
|
|
300
|
+
ondragover={is_enabled.then_some(dragover)}
|
|
301
|
+
ondragenter={is_enabled.then_some(drag_container.dragenter)}
|
|
302
|
+
ondragleave={is_enabled.then_some(drag_container.dragleave)}
|
|
292
303
|
ref={drag_container.noderef}
|
|
293
|
-
ondrop={drop}
|
|
304
|
+
ondrop={is_enabled.then_some(drop)}
|
|
294
305
|
>
|
|
295
306
|
<div class="psp-text-field">
|
|
296
307
|
<ul class="psp-text-field__input" for={ctx.props().name}>
|
|
297
308
|
{ columns_html }
|
|
298
|
-
if ctx.props().is_dragover.is_none()
|
|
309
|
+
if ctx.props().disabled && ctx.props().is_dragover.is_none() {
|
|
310
|
+
<div class="pivot-column">
|
|
311
|
+
<div class="pivot-column-border pivot-column-total">
|
|
312
|
+
<TypeIcon ty={ColumnType::Integer} />
|
|
313
|
+
<span class="column_name">{ "TOTAL" }</span>
|
|
314
|
+
</div>
|
|
315
|
+
<span
|
|
316
|
+
class="toggle-mode is_column_active"
|
|
317
|
+
onmousedown={ctx.props().parent.callback(move |_| V::close(0))}
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
} else if ctx.props().is_dragover.is_none() | (!invalid_drag && valid_duplicate_drag) {
|
|
299
321
|
<EmptyColumn {column_dropdown} {exclude} {on_select} />
|
|
300
322
|
} else if invalid_drag {
|
|
301
323
|
<InvalidColumn />
|
|
@@ -34,6 +34,9 @@ pub struct ScrollPanelProps {
|
|
|
34
34
|
#[prop_or_default]
|
|
35
35
|
pub viewport_ref: Option<NodeRef>,
|
|
36
36
|
|
|
37
|
+
#[prop_or_default]
|
|
38
|
+
pub initial_width: Option<f64>,
|
|
39
|
+
|
|
37
40
|
#[prop_or_default]
|
|
38
41
|
pub class: Classes,
|
|
39
42
|
|
|
@@ -52,6 +55,9 @@ pub struct ScrollPanelProps {
|
|
|
52
55
|
#[prop_or_default]
|
|
53
56
|
pub on_resize: Option<Rc<PubSub<()>>>,
|
|
54
57
|
|
|
58
|
+
#[prop_or_default]
|
|
59
|
+
pub on_auto_width: Callback<f64>,
|
|
60
|
+
|
|
55
61
|
#[prop_or_default]
|
|
56
62
|
pub on_dimensions_reset: Option<Rc<PubSub<()>>>,
|
|
57
63
|
|
|
@@ -125,7 +131,7 @@ impl Component for ScrollPanel {
|
|
|
125
131
|
Self {
|
|
126
132
|
viewport_ref: Default::default(),
|
|
127
133
|
viewport_height: 0f64,
|
|
128
|
-
viewport_width:
|
|
134
|
+
viewport_width: ctx.props().initial_width.unwrap_or_default(),
|
|
129
135
|
content_window: None,
|
|
130
136
|
needs_rerender: true,
|
|
131
137
|
total_height,
|
|
@@ -150,6 +156,7 @@ impl Component for ScrollPanel {
|
|
|
150
156
|
|
|
151
157
|
self.viewport_height = rect.height() - 8.0;
|
|
152
158
|
self.viewport_width = self.viewport_width.max(rect.width() - 6.0);
|
|
159
|
+
ctx.props().on_auto_width.emit(self.viewport_width);
|
|
153
160
|
re_render
|
|
154
161
|
},
|
|
155
162
|
ScrollPanelMsg::CalculateWindowContent => self.calculate_window_content(ctx),
|
|
@@ -100,12 +100,12 @@ where
|
|
|
100
100
|
ctx.props().on_select.emit(self.selected.clone());
|
|
101
101
|
return true;
|
|
102
102
|
}
|
|
103
|
-
} else if code.as_str() == "ArrowDown"
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
} else if code.as_str() == "ArrowDown"
|
|
104
|
+
&& let Some(x) = find_nth(idx + 1, &ctx.props().values)
|
|
105
|
+
{
|
|
106
|
+
self.selected = x.clone();
|
|
107
|
+
ctx.props().on_select.emit(self.selected.clone());
|
|
108
|
+
return true;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -220,9 +220,9 @@ where
|
|
|
220
220
|
};
|
|
221
221
|
|
|
222
222
|
let value = if ctx.props().is_autosize {
|
|
223
|
-
self.selected.to_string()
|
|
223
|
+
Some(self.selected.to_string())
|
|
224
224
|
} else {
|
|
225
|
-
|
|
225
|
+
None
|
|
226
226
|
};
|
|
227
227
|
|
|
228
228
|
html! {
|
|
@@ -230,7 +230,7 @@ where
|
|
|
230
230
|
<label>
|
|
231
231
|
{ ctx.props().label.as_ref().map(|x| x.to_string()).unwrap_or_default() }
|
|
232
232
|
</label>
|
|
233
|
-
<div class={wrapper_class} data-value={value
|
|
233
|
+
<div class={wrapper_class} data-value={value}>{ select }</div>
|
|
234
234
|
} else {
|
|
235
235
|
<div class={wrapper_class} data-value={value}>{ select }</div>
|
|
236
236
|
}
|
|
@@ -201,11 +201,11 @@ impl Component for SplitPanel {
|
|
|
201
201
|
let count = iter.len();
|
|
202
202
|
let contents = html! {
|
|
203
203
|
<>
|
|
204
|
-
<LocalStyle
|
|
204
|
+
<LocalStyle href={css!("containers/split-panel")} />
|
|
205
205
|
for (i, x) in iter {
|
|
206
206
|
if i == 0 {
|
|
207
207
|
if count == 1 {
|
|
208
|
-
<key=
|
|
208
|
+
<key=0>
|
|
209
209
|
{x}
|
|
210
210
|
</>
|
|
211
211
|
} else {
|
|
@@ -87,19 +87,19 @@ impl Component for FilterDropDown {
|
|
|
87
87
|
},
|
|
88
88
|
FilterDropDownMsg::ItemDown => {
|
|
89
89
|
self.selected += 1;
|
|
90
|
-
if let Some(ref values) = self.values
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
if let Some(ref values) = self.values
|
|
91
|
+
&& self.selected >= values.len()
|
|
92
|
+
{
|
|
93
|
+
self.selected = 0;
|
|
94
94
|
};
|
|
95
95
|
|
|
96
96
|
true
|
|
97
97
|
},
|
|
98
98
|
FilterDropDownMsg::ItemUp => {
|
|
99
|
-
if let Some(ref values) = self.values
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
if let Some(ref values) = self.values
|
|
100
|
+
&& self.selected < 1
|
|
101
|
+
{
|
|
102
|
+
self.selected = values.len();
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
self.selected -= 1;
|
|
@@ -88,19 +88,19 @@ impl Component for FunctionDropDown {
|
|
|
88
88
|
},
|
|
89
89
|
FunctionDropDownMsg::ItemDown => {
|
|
90
90
|
self.selected += 1;
|
|
91
|
-
if let Some(ref values) = self.values
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
if let Some(ref values) = self.values
|
|
92
|
+
&& self.selected >= values.len()
|
|
93
|
+
{
|
|
94
|
+
self.selected = 0;
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
true
|
|
98
98
|
},
|
|
99
99
|
FunctionDropDownMsg::ItemUp => {
|
|
100
|
-
if let Some(ref values) = self.values
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
if let Some(ref values) = self.values
|
|
101
|
+
&& self.selected < 1
|
|
102
|
+
{
|
|
103
|
+
self.selected = values.len();
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
self.selected -= 1;
|
|
@@ -82,10 +82,10 @@ where
|
|
|
82
82
|
true
|
|
83
83
|
},
|
|
84
84
|
ModalMsg::SubMsg(msg) => {
|
|
85
|
-
if let Some(child) = &ctx.props().child
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
if let Some(child) = &ctx.props().child
|
|
86
|
+
&& let Some(link) = child.props.weak_link().borrow().as_ref()
|
|
87
|
+
{
|
|
88
|
+
link.send_message(msg);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
false
|
|
@@ -83,11 +83,21 @@ impl Component for PluginSelector {
|
|
|
83
83
|
let metadata =
|
|
84
84
|
renderer.get_next_plugin_metadata(&PluginUpdate::Update(plugin_name));
|
|
85
85
|
|
|
86
|
-
let
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
let prev_metadata = renderer.metadata();
|
|
87
|
+
let requirements = metadata.as_ref().unwrap_or(&*prev_metadata);
|
|
88
|
+
let rollup_features = session
|
|
89
|
+
.metadata()
|
|
90
|
+
.get_features()
|
|
91
|
+
.map(|x| x.get_group_rollup_modes())
|
|
92
|
+
.unwrap();
|
|
93
|
+
|
|
94
|
+
let group_rollups = requirements.get_group_rollups(&rollup_features);
|
|
95
|
+
let mut update = ViewConfigUpdate {
|
|
96
|
+
group_rollup_mode: group_rollups.first().cloned(),
|
|
97
|
+
..ViewConfigUpdate::default()
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
session.set_update_column_defaults(&mut update, requirements);
|
|
91
101
|
|
|
92
102
|
if let Ok(task) = ctx.props().update_and_render(update) {
|
|
93
103
|
ApiFuture::spawn(task);
|
|
@@ -164,6 +164,9 @@ impl Reducible for StatusIconState {
|
|
|
164
164
|
StatusIconStateAction::Increment | StatusIconStateAction::Decrement,
|
|
165
165
|
) => StatusIconState::Loading,
|
|
166
166
|
(_, StatusIconStateAction::Increment) => Self::Updating(1),
|
|
167
|
+
(StatusIconState::Errored(x, y, z), _) => {
|
|
168
|
+
StatusIconState::Errored(x.clone(), y.clone(), z)
|
|
169
|
+
},
|
|
167
170
|
(_, StatusIconStateAction::Decrement) => StatusIconState::Normal,
|
|
168
171
|
};
|
|
169
172
|
|
|
@@ -93,10 +93,10 @@ impl FilterDropDownElement {
|
|
|
93
93
|
FilterDropDownMsg::SetValues(values),
|
|
94
94
|
]);
|
|
95
95
|
|
|
96
|
-
if let Some(x) = self.target.borrow().clone()
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
if let Some(x) = self.target.borrow().clone()
|
|
97
|
+
&& !self.modal.is_open()
|
|
98
|
+
{
|
|
99
|
+
ApiFuture::spawn(self.modal.clone().open(x, None))
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
},
|
|
@@ -77,9 +77,6 @@ pub struct PerspectiveViewerElement {
|
|
|
77
77
|
_subscriptions: Rc<[Subscription; 2]>,
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
// derive_model!( Renderer, Root, Session, Presentation for
|
|
81
|
-
// PerspectiveViewerElement);
|
|
82
|
-
|
|
83
80
|
impl CustomElementMetadata for PerspectiveViewerElement {
|
|
84
81
|
const CUSTOM_ELEMENT_NAME: &'static str = "perspective-viewer";
|
|
85
82
|
const STATICS: &'static [&'static str] = ["registerPlugin", "getExprTKCommands"].as_slice();
|
|
@@ -939,10 +936,8 @@ impl PerspectiveViewerElement {
|
|
|
939
936
|
.cloned();
|
|
940
937
|
|
|
941
938
|
changed = presentation.set_theme_name(reset_theme.as_deref()).await? || changed;
|
|
942
|
-
if changed {
|
|
943
|
-
|
|
944
|
-
return renderer.restyle_all(&view).await;
|
|
945
|
-
}
|
|
939
|
+
if changed && let Some(view) = session.get_view() {
|
|
940
|
+
return renderer.restyle_all(&view).await;
|
|
946
941
|
}
|
|
947
942
|
|
|
948
943
|
Ok(JsValue::UNDEFINED)
|
package/src/rust/js/plugin.rs
CHANGED
|
@@ -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::config::GroupRollupMode;
|
|
13
14
|
use perspective_js::utils::*;
|
|
14
15
|
use serde::*;
|
|
15
16
|
use wasm_bindgen::prelude::*;
|
|
@@ -61,6 +62,9 @@ extern "C" {
|
|
|
61
62
|
#[wasm_bindgen(method, getter)]
|
|
62
63
|
pub fn priority(this: &JsPerspectiveViewerPlugin) -> Option<i32>;
|
|
63
64
|
|
|
65
|
+
#[wasm_bindgen(method, getter)]
|
|
66
|
+
pub fn group_rollups(this: &JsPerspectiveViewerPlugin) -> Option<js_sys::Array>;
|
|
67
|
+
|
|
64
68
|
/// Don't call this method directly. Instead, call the corresponding method on the PluginColumnStyles model.
|
|
65
69
|
#[wasm_bindgen(method, catch)]
|
|
66
70
|
pub fn can_render_column_styles(this: &JsPerspectiveViewerPlugin, view_type: &str, group: Option<&str>) -> ApiResult<bool>;
|
|
@@ -146,6 +150,7 @@ pub struct ViewConfigRequirements {
|
|
|
146
150
|
pub max_cells: Option<usize>,
|
|
147
151
|
pub name: String,
|
|
148
152
|
pub render_warning: bool,
|
|
153
|
+
group_rollups: Option<Vec<GroupRollupMode>>,
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
impl ViewConfigRequirements {
|
|
@@ -155,6 +160,17 @@ impl ViewConfigRequirements {
|
|
|
155
160
|
.map(|x| index < x.len() - 1)
|
|
156
161
|
.unwrap_or(false)
|
|
157
162
|
}
|
|
163
|
+
|
|
164
|
+
pub fn get_group_rollups(&self, rollup_features: &[GroupRollupMode]) -> Vec<GroupRollupMode> {
|
|
165
|
+
self.group_rollups
|
|
166
|
+
.clone()
|
|
167
|
+
.map(|x| {
|
|
168
|
+
x.into_iter()
|
|
169
|
+
.filter(|y| rollup_features.is_empty() || rollup_features.contains(y))
|
|
170
|
+
.collect()
|
|
171
|
+
})
|
|
172
|
+
.unwrap_or_default()
|
|
173
|
+
}
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
impl JsPerspectiveViewerPlugin {
|
|
@@ -169,6 +185,9 @@ impl JsPerspectiveViewerPlugin {
|
|
|
169
185
|
max_cells: self.max_cells(),
|
|
170
186
|
name: self.name(),
|
|
171
187
|
render_warning: self.render_warning().unwrap_or(true),
|
|
188
|
+
group_rollups: self
|
|
189
|
+
.group_rollups()
|
|
190
|
+
.map(|x| x.into_serde_ext::<Vec<GroupRollupMode>>().unwrap()),
|
|
172
191
|
})
|
|
173
192
|
}
|
|
174
193
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
use std::collections::HashSet;
|
|
14
14
|
|
|
15
|
+
use base64::prelude::*;
|
|
15
16
|
use futures::join;
|
|
16
17
|
use itertools::Itertools;
|
|
17
18
|
use perspective_client::ViewWindow;
|
|
@@ -59,7 +60,7 @@ pub trait CopyExportModel:
|
|
|
59
60
|
let mut config = config?;
|
|
60
61
|
config.settings = false;
|
|
61
62
|
let js_config = serde_json::to_string(&config)?;
|
|
62
|
-
let html = export_app::render(&
|
|
63
|
+
let html = export_app::render(&BASE64_STANDARD.encode(arrow), &js_config, &plugins);
|
|
63
64
|
Ok(js_sys::JsString::from(html.trim()).into())
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -38,8 +38,9 @@ impl IntersectionObserverHandle {
|
|
|
38
38
|
) -> Self {
|
|
39
39
|
clone!(session, renderer, presentation);
|
|
40
40
|
let _callback = Closure::new(move |xs: js_sys::Array| {
|
|
41
|
+
// https://stackoverflow.com/questions/53862160/intersectionobserver-multiple-entries
|
|
41
42
|
let intersect = xs
|
|
42
|
-
.
|
|
43
|
+
.pop()
|
|
43
44
|
.unchecked_into::<IntersectionObserverEntry>()
|
|
44
45
|
.is_intersecting();
|
|
45
46
|
|
|
@@ -49,6 +50,7 @@ impl IntersectionObserverHandle {
|
|
|
49
50
|
session,
|
|
50
51
|
renderer,
|
|
51
52
|
};
|
|
53
|
+
|
|
52
54
|
ApiFuture::spawn(state.set_pause(intersect));
|
|
53
55
|
});
|
|
54
56
|
|
|
@@ -46,7 +46,10 @@ fn fill_rule_theme_names(
|
|
|
46
46
|
if property == "--theme-name" {
|
|
47
47
|
let name = style.get_property_value("--theme-name")?;
|
|
48
48
|
let trimmed = name.trim();
|
|
49
|
-
|
|
49
|
+
let theme = &trimmed[1..trimmed.len() - 1];
|
|
50
|
+
if themes.iter().find(|x| x == &theme).is_none() {
|
|
51
|
+
themes.push(theme.to_owned());
|
|
52
|
+
}
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
}
|