@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.
Files changed (106) hide show
  1. package/dist/cdn/perspective-viewer.js +6 -6
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/esm/perspective-viewer.inline.js +9 -9
  24. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  25. package/dist/esm/perspective-viewer.js +1 -1
  26. package/dist/esm/perspective-viewer.js.map +2 -2
  27. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +2 -0
  28. package/dist/esm/ts-rs/ViewerConfig.d.ts +2 -0
  29. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +9 -0
  30. package/dist/esm/ts-rs/WindowAggregate.d.ts +1 -0
  31. package/dist/esm/ts-rs/WindowSort.d.ts +7 -0
  32. package/dist/esm/ts-rs/WindowSortDir.d.ts +6 -0
  33. package/dist/esm/ts-rs/WindowSpec.d.ts +46 -0
  34. package/dist/esm/ts-rs/Windows.d.ts +10 -0
  35. package/dist/wasm/perspective-viewer.d.ts +103 -16
  36. package/dist/wasm/perspective-viewer.js +118 -108
  37. package/dist/wasm/perspective-viewer.wasm +0 -0
  38. package/dist/wasm/perspective-viewer.wasm.d.ts +15 -15
  39. package/package.json +1 -1
  40. package/src/css/column-selector.css +1 -0
  41. package/src/css/column-settings-panel.css +67 -2
  42. package/src/css/column-style.css +4 -3
  43. package/src/css/config-selector.css +22 -1
  44. package/src/css/containers/context-menu.css +12 -6
  45. package/src/css/containers/tabs.css +2 -2
  46. package/src/css/viewer.css +8 -0
  47. package/src/rust/components/column_selector/active_column.rs +21 -27
  48. package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
  49. package/src/rust/components/column_selector/config_selector.rs +7 -1
  50. package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
  51. package/src/rust/components/column_selector/inactive_column.rs +8 -1
  52. package/src/rust/components/column_selector.rs +20 -3
  53. package/src/rust/components/column_settings_sidebar/style_tab.rs +2 -1
  54. package/src/rust/components/column_settings_sidebar/window_tab.rs +34 -0
  55. package/src/rust/components/column_settings_sidebar.rs +163 -16
  56. package/src/rust/components/containers/dragdrop_list.rs +73 -3
  57. package/src/rust/components/containers/sidebar.rs +99 -22
  58. package/src/rust/components/containers/split_panel.rs +6 -0
  59. package/src/rust/components/editable_header.rs +2 -0
  60. package/src/rust/components/main_panel.rs +5 -0
  61. package/src/rust/components/mod.rs +1 -0
  62. package/src/rust/components/panel_menu.rs +7 -0
  63. package/src/rust/components/panel_tab.rs +5 -0
  64. package/src/rust/components/portal.rs +7 -0
  65. package/src/rust/components/viewer/msg.rs +2 -0
  66. package/src/rust/components/viewer/render.rs +12 -0
  67. package/src/rust/components/viewer/settings.rs +33 -1
  68. package/src/rust/components/viewer.rs +2 -0
  69. package/src/rust/components/window_editor.rs +1013 -0
  70. package/src/rust/lib.rs +4 -1
  71. package/src/rust/presentation/column_locator.rs +16 -1
  72. package/src/rust/queries/columns_iter_set.rs +7 -1
  73. package/src/rust/renderer/dispatch.rs +85 -58
  74. package/src/rust/renderer.rs +34 -1
  75. package/src/rust/session/drag_drop_update.rs +18 -0
  76. package/src/rust/session/metadata.rs +66 -9
  77. package/src/rust/session/replace_expression_update.rs +1 -0
  78. package/src/rust/session.rs +7 -5
  79. package/src/rust/tasks/edit_window.rs +155 -0
  80. package/src/rust/tasks/mod.rs +2 -0
  81. package/src/rust/utils/browser/dragdrop.rs +12 -0
  82. package/src/rust/utils/browser/tests/debounce.rs +49 -0
  83. package/src/rust/utils/debounce.rs +76 -27
  84. package/src/svg/pin-icon.svg +5 -0
  85. package/src/themes/icons.css +1 -0
  86. package/src/themes/intl/de.css +7 -0
  87. package/src/themes/intl/es.css +7 -0
  88. package/src/themes/intl/fr.css +7 -0
  89. package/src/themes/intl/ja.css +7 -0
  90. package/src/themes/intl/pt.css +7 -0
  91. package/src/themes/intl/zh.css +7 -0
  92. package/src/themes/intl.css +10 -1
  93. package/src/ts/ts-rs/PanelViewerConfig.ts +2 -1
  94. package/src/ts/ts-rs/ViewerConfig.ts +2 -1
  95. package/src/ts/ts-rs/ViewerConfigUpdate.ts +9 -0
  96. package/src/ts/ts-rs/WindowAggregate.ts +3 -0
  97. package/src/ts/ts-rs/WindowSort.ts +9 -0
  98. package/src/ts/ts-rs/WindowSortDir.ts +8 -0
  99. package/src/ts/ts-rs/WindowSpec.ts +46 -0
  100. package/src/ts/ts-rs/Windows.ts +10 -0
  101. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline0.js +0 -0
  102. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline1.js +0 -0
  103. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline2.js +0 -0
  104. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline3.js +0 -0
  105. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline4.js +0 -0
  106. /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, "Attributes");
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
  }
@@ -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: 4px 0px;
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
- --psp-context-menu--hover--background,
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: 12px 0px 12px 8px;
31
+ padding: 13px 0px 12px 8px;
32
32
  }
33
33
 
34
34
  .tab-section {
35
- padding: 12px 0px 0px 8px;
35
+ padding: 6px 0 0 8px;
36
36
  flex: 0 0 auto;
37
37
  overflow: hidden;
38
38
  }
@@ -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!["column-selector-draggable"];
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
- <div
367
- class={classes}
368
- ref={&self.add_expression_ref}
369
- draggable="true"
370
- {ondragstart}
371
- {ondragend}
372
- >
373
- <div class="column-selector-column-border">
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
- <span
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
- </div>
402
- </div>
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.is_expression {
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
- is_disabled={!is_expression}
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)) | Some(ColumnLocator::Expression(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!(selected_column, Some(ColumnLocator::Expression(x)) if x.as_str() == vc.name);
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
- Some(html! { <fieldset class="style-control">{ component }</fieldset> })
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
+ }