@perspective-dev/viewer 5.0.0 → 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 +6 -6
- package/dist/cdn/perspective-viewer.js.map +2 -2
- 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/perspective-viewer.inline.js +9 -9
- package/dist/esm/perspective-viewer.inline.js.map +2 -2
- package/dist/esm/perspective-viewer.js +1 -1
- package/dist/esm/perspective-viewer.js.map +2 -2
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +2 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +2 -0
- 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/wasm/perspective-viewer.d.ts +103 -16
- package/dist/wasm/perspective-viewer.js +118 -108
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +15 -15
- package/package.json +1 -1
- package/src/css/column-selector.css +1 -0
- package/src/css/column-settings-panel.css +67 -2
- package/src/css/column-style.css +4 -3
- package/src/css/config-selector.css +22 -1
- package/src/css/containers/context-menu.css +12 -6
- package/src/css/containers/tabs.css +2 -2
- package/src/css/viewer.css +8 -0
- package/src/rust/components/column_selector/active_column.rs +21 -27
- package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
- package/src/rust/components/column_selector/config_selector.rs +7 -1
- package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
- package/src/rust/components/column_selector/inactive_column.rs +8 -1
- package/src/rust/components/column_selector.rs +20 -3
- 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 +6 -0
- package/src/rust/components/editable_header.rs +2 -0
- package/src/rust/components/main_panel.rs +5 -0
- package/src/rust/components/mod.rs +1 -0
- package/src/rust/components/panel_menu.rs +7 -0
- package/src/rust/components/panel_tab.rs +5 -0
- package/src/rust/components/portal.rs +7 -0
- package/src/rust/components/viewer/msg.rs +2 -0
- package/src/rust/components/viewer/render.rs +12 -0
- package/src/rust/components/viewer/settings.rs +33 -1
- package/src/rust/components/viewer.rs +2 -0
- package/src/rust/components/window_editor.rs +1013 -0
- package/src/rust/lib.rs +4 -1
- package/src/rust/presentation/column_locator.rs +16 -1
- package/src/rust/queries/columns_iter_set.rs +7 -1
- package/src/rust/renderer/dispatch.rs +85 -58
- package/src/rust/renderer.rs +34 -1
- 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.rs +7 -5
- package/src/rust/tasks/edit_window.rs +155 -0
- package/src/rust/tasks/mod.rs +2 -0
- package/src/rust/utils/browser/dragdrop.rs +12 -0
- package/src/rust/utils/browser/tests/debounce.rs +49 -0
- package/src/rust/utils/debounce.rs +76 -27
- package/src/svg/pin-icon.svg +5 -0
- package/src/themes/icons.css +1 -0
- 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/ts/ts-rs/PanelViewerConfig.ts +2 -1
- package/src/ts/ts-rs/ViewerConfig.ts +2 -1
- 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/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline5.js +0 -0
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
.item_title {
|
|
42
42
|
flex: 0 0 auto;
|
|
43
43
|
font-size: var(--label--font-size, 0.75em);
|
|
44
|
+
margin-top: 9px;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
input {
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
|
|
71
72
|
.sidebar_header_contents {
|
|
72
73
|
display: flex;
|
|
73
|
-
margin: 8px;
|
|
74
|
+
margin: 7px 8px;
|
|
74
75
|
align-items: center;
|
|
75
76
|
border-radius: 3px;
|
|
76
77
|
outline-width: 1px;
|
|
@@ -117,6 +118,18 @@
|
|
|
117
118
|
content: var(--psp-label--color--content, "Color");
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
label#window-frame-label:before {
|
|
122
|
+
content: var(--psp-label--window-frame--content, "Frame");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
label#window-offset-label:before {
|
|
126
|
+
content: var(--psp-label--window-offset--content, "Offset");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
label#window-alpha-label:before {
|
|
130
|
+
content: var(--psp-label--window-alpha--content, "Alpha");
|
|
131
|
+
}
|
|
132
|
+
|
|
120
133
|
label#format-label:before {
|
|
121
134
|
content: var(--psp-label--format--content, "Format");
|
|
122
135
|
}
|
|
@@ -296,6 +309,58 @@
|
|
|
296
309
|
}
|
|
297
310
|
|
|
298
311
|
div.tab-title#Attributes:before {
|
|
299
|
-
content: var(--psp-label--attributes-tab--content, "
|
|
312
|
+
content: var(--psp-label--attributes-tab--content, "Expression");
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
div.tab-title#Window:before {
|
|
316
|
+
content: var(--psp-label--window-tab--content, "Window");
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
#window-tab {
|
|
321
|
+
display: flex;
|
|
322
|
+
flex-direction: column;
|
|
323
|
+
flex: 1;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
#window-editor-container .window-editor-error {
|
|
327
|
+
color: var(--error--color, #ff5942);
|
|
328
|
+
font-size: 11px;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
:host {
|
|
332
|
+
#column_settings_sidebar {
|
|
333
|
+
.sidebar_header {
|
|
334
|
+
padding-right: 24px;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
#window-order-by .column-selector-column-border {
|
|
339
|
+
padding-right: 8px;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.sidebar_pin_button {
|
|
343
|
+
position: absolute;
|
|
344
|
+
right: 6px;
|
|
345
|
+
width: 14px;
|
|
346
|
+
height: 14px;
|
|
347
|
+
z-index: 8;
|
|
348
|
+
cursor: pointer;
|
|
349
|
+
background-color: var(--psp--color);
|
|
350
|
+
opacity: 0.5;
|
|
351
|
+
background-repeat: no-repeat;
|
|
352
|
+
-webkit-mask-size: cover;
|
|
353
|
+
mask-size: cover;
|
|
354
|
+
-webkit-mask-image: var(--psp-icon--pin--mask-image);
|
|
355
|
+
mask-image: var(--psp-icon--pin--mask-image);
|
|
356
|
+
transform: rotate(45deg);
|
|
357
|
+
&:hover {
|
|
358
|
+
opacity: 1;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
&.is-pinned {
|
|
362
|
+
transform: none;
|
|
363
|
+
opacity: 1;
|
|
364
|
+
}
|
|
300
365
|
}
|
|
301
366
|
}
|
package/src/css/column-style.css
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
:host {
|
|
15
15
|
#column-style-container,
|
|
16
|
-
#plugin-config-container
|
|
16
|
+
#plugin-config-container,
|
|
17
|
+
#window-editor-container {
|
|
17
18
|
outline: none;
|
|
18
19
|
user-select: none;
|
|
19
20
|
|
|
@@ -81,8 +82,8 @@
|
|
|
81
82
|
label {
|
|
82
83
|
display: block;
|
|
83
84
|
font-size: var(--label--font-size, 0.75em);
|
|
84
|
-
margin: 4px 0 2px 0;
|
|
85
85
|
width: 100%;
|
|
86
|
+
margin: 4px 0 2px 0;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
span.reset-default-style-disabled {
|
|
@@ -160,7 +161,7 @@
|
|
|
160
161
|
|
|
161
162
|
.column-style-label {
|
|
162
163
|
display: flex;
|
|
163
|
-
padding:
|
|
164
|
+
padding: 0px;
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
.indent {
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
#top_panel
|
|
63
|
+
#top_panel,
|
|
64
|
+
#window-editor-slots {
|
|
64
65
|
display: flex;
|
|
65
66
|
flex-direction: column;
|
|
66
67
|
justify-content: stretch;
|
|
@@ -176,6 +177,7 @@
|
|
|
176
177
|
flex: 1 1 auto;
|
|
177
178
|
align-self: stretch;
|
|
178
179
|
position: relative;
|
|
180
|
+
min-height: 24px;
|
|
179
181
|
&:before {
|
|
180
182
|
content: " ";
|
|
181
183
|
position: absolute;
|
|
@@ -215,6 +217,21 @@
|
|
|
215
217
|
content: var(--psp-label--filter--content, "Where");
|
|
216
218
|
}
|
|
217
219
|
|
|
220
|
+
#window-source label.pivot-selector-label:before {
|
|
221
|
+
content: var(--psp-label--window-source--content, "Column");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
#window-order-by label.pivot-selector-label:before {
|
|
225
|
+
content: var(--psp-label--window-order-by--content, "Order By");
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#window-partition-by label.pivot-selector-label:before {
|
|
229
|
+
content: var(
|
|
230
|
+
--psp-label--window-partition-by--content,
|
|
231
|
+
"Partition By"
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
218
235
|
.rrow {
|
|
219
236
|
display: flex;
|
|
220
237
|
min-height: 24px;
|
|
@@ -341,6 +358,10 @@
|
|
|
341
358
|
}
|
|
342
359
|
}
|
|
343
360
|
|
|
361
|
+
#window-editor-slots .pivot-column {
|
|
362
|
+
min-height: 24px;
|
|
363
|
+
}
|
|
364
|
+
|
|
344
365
|
.sort-icon {
|
|
345
366
|
display: inline-flex;
|
|
346
367
|
margin-left: auto;
|
|
@@ -38,12 +38,13 @@
|
|
|
38
38
|
cursor: pointer;
|
|
39
39
|
white-space: nowrap;
|
|
40
40
|
|
|
41
|
+
/* The dropdown-menu surface's inverted hover, from the same theme keys
|
|
42
|
+
* (`--psp--color`/`--psp--background-color` land on the host via the
|
|
43
|
+
* modal selector groups) — the Copy/Export pickers this menu spawns use
|
|
44
|
+
* exactly this idiom. */
|
|
41
45
|
&:hover {
|
|
42
|
-
background: var(
|
|
43
|
-
|
|
44
|
-
var(--psp-active--background, rgba(0, 0, 0, 0.08))
|
|
45
|
-
);
|
|
46
|
-
color: var(--psp-context-menu--hover--color, inherit);
|
|
46
|
+
background-color: var(--psp--color);
|
|
47
|
+
color: var(--psp--background-color);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
&.disabled {
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
cursor: default;
|
|
52
53
|
|
|
53
54
|
&:hover {
|
|
54
|
-
background: transparent;
|
|
55
|
+
background-color: transparent;
|
|
55
56
|
color: inherit;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -86,6 +87,11 @@
|
|
|
86
87
|
max-height: 50vh;
|
|
87
88
|
overflow-y: auto;
|
|
88
89
|
background-color: var(--psp--background-color, white);
|
|
90
|
+
|
|
91
|
+
/* The flyout is a CHILD of its (necessarily hovered, so inverted) parent
|
|
92
|
+
* item — un-hovered rows must not inherit the parent's inverted
|
|
93
|
+
* foreground. */
|
|
94
|
+
color: var(--psp--color);
|
|
89
95
|
border: 1px solid var(--psp-inactive--color);
|
|
90
96
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
91
97
|
}
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
display: flex;
|
|
29
29
|
flex-direction: column;
|
|
30
30
|
.tab-section:last-child {
|
|
31
|
-
padding:
|
|
31
|
+
padding: 13px 0px 12px 8px;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.tab-section {
|
|
35
|
-
padding:
|
|
35
|
+
padding: 6px 0 0 8px;
|
|
36
36
|
flex: 0 0 auto;
|
|
37
37
|
overflow: hidden;
|
|
38
38
|
}
|
package/src/css/viewer.css
CHANGED
|
@@ -303,6 +303,14 @@
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
#modal_panel.pinned {
|
|
307
|
+
position: static;
|
|
308
|
+
flex: 0 0 auto;
|
|
309
|
+
width: auto;
|
|
310
|
+
pointer-events: all;
|
|
311
|
+
overflow: visible;
|
|
312
|
+
}
|
|
313
|
+
|
|
306
314
|
#modal_panel > .split-panel-divider {
|
|
307
315
|
border-left: 1px solid var(--psp-inactive--color, #6e6e6e);
|
|
308
316
|
margin-right: -5px;
|
|
@@ -18,10 +18,10 @@ use yew::prelude::*;
|
|
|
18
18
|
|
|
19
19
|
use super::InPlaceColumn;
|
|
20
20
|
use super::aggregate_selector::*;
|
|
21
|
+
use super::column_selector_column_row::ColumnSelectorColumnRow;
|
|
21
22
|
use super::expr_edit_button::*;
|
|
22
23
|
use crate::components::column_dropdown::ColumnDropDownElement;
|
|
23
24
|
use crate::components::column_selector::{EmptyColumn, InvalidColumn};
|
|
24
|
-
use crate::components::type_icon::TypeIcon;
|
|
25
25
|
use crate::config::ColumnSelectMode;
|
|
26
26
|
use crate::presentation::{ColumnLocator, Presentation};
|
|
27
27
|
use crate::queries::*;
|
|
@@ -66,6 +66,10 @@ pub struct ActiveColumnProps {
|
|
|
66
66
|
#[prop_or_default]
|
|
67
67
|
pub is_expression: bool,
|
|
68
68
|
|
|
69
|
+
/// Whether this column is a window column.
|
|
70
|
+
#[prop_or_default]
|
|
71
|
+
pub is_window: bool,
|
|
72
|
+
|
|
69
73
|
/// Whether the expression/config edit button should be shown. Computed
|
|
70
74
|
/// by the parent (`is_expression || can_render_column_styles`).
|
|
71
75
|
#[prop_or_default]
|
|
@@ -95,6 +99,7 @@ impl PartialEq for ActiveColumnProps {
|
|
|
95
99
|
&& self.is_aggregated == rhs.is_aggregated
|
|
96
100
|
&& self.is_editing == rhs.is_editing
|
|
97
101
|
&& self.is_expression == rhs.is_expression
|
|
102
|
+
&& self.is_window == rhs.is_window
|
|
98
103
|
&& self.show_edit_btn == rhs.show_edit_btn
|
|
99
104
|
&& self.col_type == rhs.col_type
|
|
100
105
|
&& self.metadata == rhs.metadata
|
|
@@ -204,10 +209,7 @@ impl Component for ActiveColumn {
|
|
|
204
209
|
Named(String),
|
|
205
210
|
}
|
|
206
211
|
|
|
207
|
-
let mut classes = classes![
|
|
208
|
-
if ctx.props().is_aggregated {
|
|
209
|
-
classes.push("show-aggregate");
|
|
210
|
-
};
|
|
212
|
+
let mut classes = classes![];
|
|
211
213
|
|
|
212
214
|
let mut outer_classes = classes!["column-selector-column"];
|
|
213
215
|
if self.mouseover {
|
|
@@ -344,6 +346,7 @@ impl Component for ActiveColumn {
|
|
|
344
346
|
.callback(|event: MouseEvent| MouseEnter(event.which() == 0));
|
|
345
347
|
|
|
346
348
|
let is_expression = ctx.props().is_expression;
|
|
349
|
+
let is_window = ctx.props().is_window;
|
|
347
350
|
let show_edit_btn = ctx.props().show_edit_btn;
|
|
348
351
|
let mut class = ctx.props().renderer.metadata().select_mode.css();
|
|
349
352
|
if is_required {
|
|
@@ -363,17 +366,14 @@ impl Component for ActiveColumn {
|
|
|
363
366
|
ondragenter={ondragenter.clone()}
|
|
364
367
|
>
|
|
365
368
|
<span {class} onmousedown={remove_column} />
|
|
366
|
-
<
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
{
|
|
371
|
-
{
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
<span class="drag-handle icon" />
|
|
375
|
-
<TypeIcon ty={col_type} />
|
|
376
|
-
if ctx.props().is_aggregated {
|
|
369
|
+
<ColumnSelectorColumnRow
|
|
370
|
+
name={name.clone()}
|
|
371
|
+
col_type={Some(col_type)}
|
|
372
|
+
wrapper_class={classes}
|
|
373
|
+
wrapper_ref={&self.add_expression_ref}
|
|
374
|
+
ondragstart={Some(ondragstart)}
|
|
375
|
+
ondragend={Some(ondragend.clone())}
|
|
376
|
+
aggregate={ctx.props().is_aggregated.then(|| html! {
|
|
377
377
|
<AggregateSelector
|
|
378
378
|
column={name.clone()}
|
|
379
379
|
aggregate={ctx.props().get_aggregate(&name)}
|
|
@@ -382,24 +382,18 @@ impl Component for ActiveColumn {
|
|
|
382
382
|
renderer={&ctx.props().renderer}
|
|
383
383
|
session={&ctx.props().session}
|
|
384
384
|
/>
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
class="column_name"
|
|
388
|
-
>
|
|
389
|
-
{ name.clone() }
|
|
390
|
-
</span>
|
|
391
|
-
if !ctx.props().is_aggregated {
|
|
392
|
-
<span class="column-selector--spacer" />
|
|
393
|
-
}
|
|
385
|
+
})}
|
|
386
|
+
trailing={html! {
|
|
394
387
|
<ExprEditButton
|
|
388
|
+
{is_window}
|
|
395
389
|
name={name.clone()}
|
|
396
390
|
on_open_expr_panel={&ctx.props().on_open_expr_panel}
|
|
397
391
|
{is_expression}
|
|
398
392
|
is_disabled={!show_edit_btn}
|
|
399
393
|
is_editing={ctx.props().is_editing}
|
|
400
394
|
/>
|
|
401
|
-
|
|
402
|
-
|
|
395
|
+
}}
|
|
396
|
+
/>
|
|
403
397
|
</div>
|
|
404
398
|
}
|
|
405
399
|
},
|
|
@@ -0,0 +1,73 @@
|
|
|
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 perspective_client::config::ColumnType;
|
|
14
|
+
use web_sys::DragEvent;
|
|
15
|
+
use yew::prelude::*;
|
|
16
|
+
|
|
17
|
+
use crate::components::type_icon::TypeIcon;
|
|
18
|
+
|
|
19
|
+
/// The draggable row of a `column-selector-column`
|
|
20
|
+
#[derive(Clone, PartialEq, Properties)]
|
|
21
|
+
pub struct ColumnSelectorColumnRowProps {
|
|
22
|
+
pub name: String,
|
|
23
|
+
|
|
24
|
+
#[prop_or_default]
|
|
25
|
+
pub col_type: Option<ColumnType>,
|
|
26
|
+
|
|
27
|
+
#[prop_or_default]
|
|
28
|
+
pub aggregate: Option<Html>,
|
|
29
|
+
|
|
30
|
+
/// Trailing affordance (e.g. the edit button in the active list).
|
|
31
|
+
#[prop_or_default]
|
|
32
|
+
pub trailing: Html,
|
|
33
|
+
|
|
34
|
+
#[prop_or_default]
|
|
35
|
+
pub wrapper_class: Classes,
|
|
36
|
+
|
|
37
|
+
#[prop_or_default]
|
|
38
|
+
pub wrapper_ref: NodeRef,
|
|
39
|
+
|
|
40
|
+
#[prop_or_default]
|
|
41
|
+
pub ondragstart: Option<Callback<DragEvent>>,
|
|
42
|
+
|
|
43
|
+
#[prop_or_default]
|
|
44
|
+
pub ondragend: Option<Callback<DragEvent>>,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[function_component]
|
|
48
|
+
pub fn ColumnSelectorColumnRow(p: &ColumnSelectorColumnRowProps) -> Html {
|
|
49
|
+
let mut classes = classes!["column-selector-draggable"];
|
|
50
|
+
if p.aggregate.is_some() {
|
|
51
|
+
classes.push("show-aggregate");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
classes.extend(p.wrapper_class.clone());
|
|
55
|
+
html! {
|
|
56
|
+
<div
|
|
57
|
+
class={classes}
|
|
58
|
+
ref={&p.wrapper_ref}
|
|
59
|
+
draggable={p.ondragstart.is_some().then_some("true")}
|
|
60
|
+
ondragstart={p.ondragstart.clone()}
|
|
61
|
+
ondragend={p.ondragend.clone()}
|
|
62
|
+
>
|
|
63
|
+
<div class="column-selector-column-border">
|
|
64
|
+
<span class="drag-handle icon" />
|
|
65
|
+
<TypeIcon ty={p.col_type.unwrap_or(ColumnType::String)} />
|
|
66
|
+
if let Some(aggregate) = &p.aggregate { { aggregate.clone() } }
|
|
67
|
+
<span class="column_name">{ p.name.clone() }</span>
|
|
68
|
+
if p.aggregate.is_none() { <span class="column-selector--spacer" /> }
|
|
69
|
+
{ p.trailing.clone() }
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -235,7 +235,7 @@ impl Component for ConfigSelector {
|
|
|
235
235
|
},
|
|
236
236
|
ConfigSelectorMsg::Close(..) => false,
|
|
237
237
|
ConfigSelectorMsg::Drop(column, action, effect, index)
|
|
238
|
-
if action != DragTarget::Active =>
|
|
238
|
+
if action != DragTarget::Active && !action.is_staged() =>
|
|
239
239
|
{
|
|
240
240
|
let col_type = ctx
|
|
241
241
|
.props()
|
|
@@ -369,6 +369,12 @@ impl Component for ConfigSelector {
|
|
|
369
369
|
ctx.props().onselect.emit(());
|
|
370
370
|
false
|
|
371
371
|
},
|
|
372
|
+
ConfigSelectorMsg::New(
|
|
373
|
+
DragTarget::WindowSource
|
|
374
|
+
| DragTarget::WindowOrderBy
|
|
375
|
+
| DragTarget::WindowPartitionBy,
|
|
376
|
+
_,
|
|
377
|
+
) => false,
|
|
372
378
|
ConfigSelectorMsg::New(DragTarget::Filter, InPlaceColumn::Column(column)) => {
|
|
373
379
|
let mut view_config = (*ctx.props().view_config).clone();
|
|
374
380
|
let op = ctx.props().default_op(column.as_str()).unwrap_or_default();
|
|
@@ -22,6 +22,10 @@ pub struct ExprEditButtonProps {
|
|
|
22
22
|
/// Is this an expression column?
|
|
23
23
|
pub is_expression: bool,
|
|
24
24
|
|
|
25
|
+
/// Is this a window column?
|
|
26
|
+
#[prop_or_default]
|
|
27
|
+
pub is_window: bool,
|
|
28
|
+
|
|
25
29
|
/// Fires when the config/expresison button is clicked.
|
|
26
30
|
pub on_open_expr_panel: Callback<ColumnLocator>,
|
|
27
31
|
|
|
@@ -38,7 +42,9 @@ pub struct ExprEditButtonProps {
|
|
|
38
42
|
#[function_component]
|
|
39
43
|
pub fn ExprEditButton(p: &ExprEditButtonProps) -> Html {
|
|
40
44
|
let onmousedown = yew::use_callback(p.clone(), |_, p| {
|
|
41
|
-
let name = if p.
|
|
45
|
+
let name = if p.is_window {
|
|
46
|
+
ColumnLocator::Window(p.name.clone())
|
|
47
|
+
} else if p.is_expression {
|
|
42
48
|
ColumnLocator::Expression(p.name.clone())
|
|
43
49
|
} else {
|
|
44
50
|
ColumnLocator::Table(p.name.clone())
|
|
@@ -43,6 +43,10 @@ pub struct InactiveColumnProps {
|
|
|
43
43
|
#[prop_or_default]
|
|
44
44
|
pub is_expression: bool,
|
|
45
45
|
|
|
46
|
+
/// Whether this column is a window column.
|
|
47
|
+
#[prop_or_default]
|
|
48
|
+
pub is_window: bool,
|
|
49
|
+
|
|
46
50
|
/// Session metadata snapshot — threaded from `SessionProps`.
|
|
47
51
|
pub metadata: SessionMetadataRc,
|
|
48
52
|
|
|
@@ -72,6 +76,7 @@ impl PartialEq for InactiveColumnProps {
|
|
|
72
76
|
&& self.name == rhs.name
|
|
73
77
|
&& self.is_editing == rhs.is_editing
|
|
74
78
|
&& self.is_expression == rhs.is_expression
|
|
79
|
+
&& self.is_window == rhs.is_window
|
|
75
80
|
&& self.metadata == rhs.metadata
|
|
76
81
|
&& self.view_config == rhs.view_config
|
|
77
82
|
}
|
|
@@ -148,6 +153,7 @@ impl Component for InactiveColumn {
|
|
|
148
153
|
.callback(|event: MouseEvent| MouseEnter(event.which() == 0));
|
|
149
154
|
|
|
150
155
|
let is_expression = ctx.props().is_expression;
|
|
156
|
+
let is_window = ctx.props().is_window;
|
|
151
157
|
|
|
152
158
|
let mut is_active_class = ctx.props().renderer.metadata().select_mode.css();
|
|
153
159
|
is_active_class.push("shift-alt-icon");
|
|
@@ -179,7 +185,8 @@ impl Component for InactiveColumn {
|
|
|
179
185
|
name={ctx.props().name.clone()}
|
|
180
186
|
on_open_expr_panel={&ctx.props().on_open_expr_panel}
|
|
181
187
|
{is_expression}
|
|
182
|
-
|
|
188
|
+
{is_window}
|
|
189
|
+
is_disabled={!(is_expression || is_window)}
|
|
183
190
|
is_editing={ctx.props().is_editing}
|
|
184
191
|
/>
|
|
185
192
|
</div>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
mod active_column;
|
|
14
14
|
mod add_expression_button;
|
|
15
15
|
mod aggregate_selector;
|
|
16
|
+
mod column_selector_column_row;
|
|
16
17
|
mod config_selector;
|
|
17
18
|
mod empty_column;
|
|
18
19
|
mod expr_edit_button;
|
|
@@ -25,6 +26,7 @@ mod sort_column;
|
|
|
25
26
|
use std::iter::*;
|
|
26
27
|
use std::rc::Rc;
|
|
27
28
|
|
|
29
|
+
pub use column_selector_column_row::*;
|
|
28
30
|
pub use empty_column::*;
|
|
29
31
|
pub use invalid_column::*;
|
|
30
32
|
use perspective_client::config::ViewConfig;
|
|
@@ -367,7 +369,9 @@ impl Component for ColumnSelector {
|
|
|
367
369
|
let column_dropdown = self.column_dropdown.clone();
|
|
368
370
|
let is_editing = matches!(
|
|
369
371
|
&ctx.props().selected_column,
|
|
370
|
-
Some(ColumnLocator::Table(x))
|
|
372
|
+
Some(ColumnLocator::Table(x))
|
|
373
|
+
| Some(ColumnLocator::Expression(x))
|
|
374
|
+
| Some(ColumnLocator::Window(x))
|
|
371
375
|
if x == &key );
|
|
372
376
|
|
|
373
377
|
// Compute metadata-derived props here so that changes to
|
|
@@ -392,10 +396,15 @@ impl Component for ColumnSelector {
|
|
|
392
396
|
.map(|n| metadata.is_column_expression(n))
|
|
393
397
|
.unwrap_or(false);
|
|
394
398
|
|
|
399
|
+
let is_window = name
|
|
400
|
+
.get_name()
|
|
401
|
+
.map(|n| metadata.is_column_window(n))
|
|
402
|
+
.unwrap_or(false);
|
|
403
|
+
|
|
395
404
|
let can_render_styles =
|
|
396
405
|
name.get_name().is_some() && renderer.can_render_column_styles();
|
|
397
406
|
|
|
398
|
-
let show_edit_btn = is_expression || can_render_styles;
|
|
407
|
+
let show_edit_btn = is_expression || is_window || can_render_styles;
|
|
399
408
|
let on_open_expr_panel = &ctx.props().on_open_expr_panel;
|
|
400
409
|
html_nested! {
|
|
401
410
|
<ScrollPanelItem {key} {size_hint}>
|
|
@@ -405,6 +414,7 @@ impl Component for ColumnSelector {
|
|
|
405
414
|
{is_aggregated}
|
|
406
415
|
{is_editing}
|
|
407
416
|
{is_expression}
|
|
417
|
+
{is_window}
|
|
408
418
|
{show_edit_btn}
|
|
409
419
|
{col_type}
|
|
410
420
|
view_config={config.clone()}
|
|
@@ -425,12 +435,18 @@ impl Component for ColumnSelector {
|
|
|
425
435
|
|
|
426
436
|
let mut inactive_children: Vec<_> = columns_iter
|
|
427
437
|
.expression()
|
|
438
|
+
.chain(columns_iter.window())
|
|
428
439
|
.chain(columns_iter.inactive())
|
|
429
440
|
.enumerate()
|
|
430
441
|
.map(|(idx, vc)| {
|
|
431
442
|
let selected_column = ctx.props().selected_column.as_ref();
|
|
432
|
-
let is_editing = matches!(
|
|
443
|
+
let is_editing = matches!(
|
|
444
|
+
selected_column,
|
|
445
|
+
Some(ColumnLocator::Expression(x)) | Some(ColumnLocator::Window(x))
|
|
446
|
+
if x.as_str() == vc.name
|
|
447
|
+
);
|
|
433
448
|
let is_expression = metadata.is_column_expression(vc.name);
|
|
449
|
+
let is_window = metadata.is_column_window(vc.name);
|
|
434
450
|
html_nested! {
|
|
435
451
|
<ScrollPanelItem key={vc.name} size_hint=28.0>
|
|
436
452
|
<InactiveColumn
|
|
@@ -439,6 +455,7 @@ impl Component for ColumnSelector {
|
|
|
439
455
|
name={vc.name.to_owned()}
|
|
440
456
|
{is_editing}
|
|
441
457
|
{is_expression}
|
|
458
|
+
{is_window}
|
|
442
459
|
view_config={config.clone()}
|
|
443
460
|
metadata={metadata.clone()}
|
|
444
461
|
onselect={&onselect}
|
|
@@ -326,7 +326,8 @@ pub fn StyleTab(props: &StyleTabProps) -> Html {
|
|
|
326
326
|
},
|
|
327
327
|
};
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
let key = format!("{}::{}", props.column_name, keys.join("+"));
|
|
330
|
+
Some(html! { <fieldset class="style-control" {key}>{ component }</fieldset> })
|
|
330
331
|
})
|
|
331
332
|
.collect_vec()
|
|
332
333
|
})
|
|
@@ -0,0 +1,34 @@
|
|
|
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 yew::prelude::*;
|
|
14
|
+
|
|
15
|
+
use super::save_settings::{SaveSettings, SaveSettingsProps};
|
|
16
|
+
use crate::components::window_editor::{WindowEditor, WindowEditorProps};
|
|
17
|
+
|
|
18
|
+
#[derive(PartialEq, Properties, Clone)]
|
|
19
|
+
pub struct WindowTabProps {
|
|
20
|
+
pub editor: WindowEditorProps,
|
|
21
|
+
pub save_section: SaveSettingsProps,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[function_component]
|
|
25
|
+
pub fn WindowTab(p: &WindowTabProps) -> Html {
|
|
26
|
+
html! {
|
|
27
|
+
<div id="window-tab">
|
|
28
|
+
<div class="tab-section" id="window-editor-section">
|
|
29
|
+
<WindowEditor ..p.editor.clone() />
|
|
30
|
+
</div>
|
|
31
|
+
<div class="tab-section"><SaveSettings ..p.save_section.clone() /></div>
|
|
32
|
+
</div>
|
|
33
|
+
}
|
|
34
|
+
}
|