@perspective-dev/viewer 4.2.0 → 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.
Files changed (45) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -0
  4. package/dist/css/themes.css +1 -1
  5. package/dist/esm/extensions.d.ts +32 -1
  6. package/dist/esm/perspective-viewer.d.ts +1 -0
  7. package/dist/esm/perspective-viewer.inline.js +2 -2
  8. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  9. package/dist/esm/perspective-viewer.js +2 -2
  10. package/dist/esm/perspective-viewer.js.map +4 -4
  11. package/dist/esm/ts-rs/GroupRollupMode.d.ts +1 -0
  12. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +2 -0
  13. package/dist/wasm/perspective-viewer.d.ts +14 -14
  14. package/dist/wasm/perspective-viewer.js +88 -80
  15. package/dist/wasm/perspective-viewer.wasm +0 -0
  16. package/dist/wasm/perspective-viewer.wasm.d.ts +14 -14
  17. package/package.json +3 -2
  18. package/src/less/column-selector.less +2 -2
  19. package/src/less/config-selector.less +66 -4
  20. package/src/rust/components/column_selector/config_selector.rs +102 -29
  21. package/src/rust/components/column_selector/pivot_column.rs +12 -7
  22. package/src/rust/components/column_selector.rs +27 -17
  23. package/src/rust/components/containers/dragdrop_list.rs +27 -6
  24. package/src/rust/components/containers/scroll_panel.rs +8 -1
  25. package/src/rust/components/containers/select.rs +3 -3
  26. package/src/rust/components/containers/split_panel.rs +2 -2
  27. package/src/rust/components/plugin_selector.rs +15 -5
  28. package/src/rust/components/status_indicator.rs +3 -0
  29. package/src/rust/js/plugin.rs +19 -0
  30. package/src/rust/model/intersection_observer.rs +3 -1
  31. package/src/rust/renderer/registry.rs +8 -1
  32. package/src/rust/session/column_defaults_update.rs +18 -0
  33. package/src/rust/session/replace_expression_update.rs +1 -0
  34. package/src/themes/botanical.less +142 -0
  35. package/src/themes/themes.less +2 -1
  36. package/src/ts/extensions.ts +73 -2
  37. package/src/ts/perspective-viewer.ts +1 -0
  38. package/src/ts/ts-rs/GroupRollupMode.ts +3 -0
  39. package/src/ts/ts-rs/ViewerConfigUpdate.ts +2 -1
  40. package/tsconfig.json +1 -0
  41. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-d729f682ba5c19df}/inline0.js +0 -0
  42. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-d729f682ba5c19df}/inline1.js +0 -0
  43. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-d729f682ba5c19df}/inline2.js +0 -0
  44. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-d729f682ba5c19df}/inline3.js +0 -0
  45. /package/dist/wasm/snippets/{perspective-viewer-1586156e058be573 → perspective-viewer-d729f682ba5c19df}/inline4.js +0 -0
@@ -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
  }
@@ -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
- .get(0)
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
 
@@ -102,7 +102,14 @@ pub impl LocalKey<Rc<RefCell<Vec<PluginRecord>>>> {
102
102
  .unwrap_or_else(|| "Custom".to_owned()),
103
103
  priority: plugin_inst.priority().unwrap_or_default(),
104
104
  };
105
+
105
106
  let mut plugins = plugin.borrow_mut();
107
+ if let Some(first) = plugins.first()
108
+ && first.tag_name.as_str() == "perspective-viewer-plugin"
109
+ {
110
+ plugins.clear();
111
+ }
112
+
106
113
  plugins.push(record);
107
114
  plugins.sort_by(|a, b| Ord::cmp(&b.priority, &a.priority));
108
115
  });
@@ -121,7 +128,7 @@ fn register_default() {
121
128
  name: "Debug".to_owned(),
122
129
  category: "Custom".to_owned(),
123
130
  tag_name: "perspective-viewer-plugin".to_owned(),
124
- priority: 0,
131
+ priority: -1,
125
132
  })
126
133
  }
127
134
  })
@@ -31,6 +31,24 @@ pub impl ViewConfigUpdate {
31
31
  columns: &[Option<String>],
32
32
  requirements: &ViewConfigRequirements,
33
33
  ) {
34
+ let rollup_features = metadata
35
+ .get_features()
36
+ .map(|x| x.get_group_rollup_modes())
37
+ .unwrap_or_default();
38
+
39
+ let group_rollups = requirements.get_group_rollups(&rollup_features);
40
+ if !group_rollups.contains(
41
+ self.group_rollup_mode
42
+ .as_ref()
43
+ .unwrap_or(&GroupRollupMode::Rollup),
44
+ ) {
45
+ self.group_rollup_mode = group_rollups.first().cloned();
46
+ tracing::error!(
47
+ "Setting plugin-advised rollup mode {:?}",
48
+ self.group_rollup_mode
49
+ );
50
+ }
51
+
34
52
  if let (
35
53
  None,
36
54
  ViewConfigRequirements {
@@ -125,6 +125,7 @@ pub impl ViewConfig {
125
125
  filter: Some(filter),
126
126
  filter_op: None,
127
127
  group_by_depth: None,
128
+ group_rollup_mode: None,
128
129
  }
129
130
  }
130
131
  }
@@ -0,0 +1,142 @@
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
+ @import "icons.less";
14
+ @import "intl.less";
15
+
16
+ @import url("ref://pro.less");
17
+
18
+ // Register theme for auto-detection
19
+ perspective-viewer,
20
+ perspective-viewer[theme="Botanical"] {
21
+ --theme-name: "Botanical";
22
+ }
23
+
24
+ perspective-viewer[theme="Botanical"] {
25
+ @include perspective-viewer-botanical;
26
+ }
27
+
28
+ perspective-copy-menu[theme="Botanical"],
29
+ perspective-export-menu[theme="Botanical"],
30
+ perspective-dropdown[theme="Botanical"],
31
+ perspective-date-column-style[theme="Botanical"],
32
+ perspective-datetime-column-style[theme="Botanical"],
33
+ perspective-number-column-style[theme="Botanical"],
34
+ perspective-string-column-style[theme="Botanical"] {
35
+ @include perspective-modal-botanical;
36
+ }
37
+
38
+ @mixin perspective-viewer-botanical {
39
+ @include perspective-viewer-pro;
40
+ @include perspective-viewer-botanical--colors;
41
+ @include perspective-viewer-botanical--datagrid;
42
+ @include perspective-viewer-botanical--d3fc;
43
+ @include perspective-viewer-botanical--openlayers;
44
+ }
45
+
46
+ @mixin perspective-modal-botanical {
47
+ @include perspective-modal-pro;
48
+ @include perspective-viewer-botanical--colors;
49
+
50
+ background-color: #1a2e1a;
51
+ border: 1px solid #3d5c3d;
52
+ }
53
+
54
+ @mixin perspective-viewer-botanical--colors {
55
+ background-color: #1a2e1a;
56
+ color: #e0ead8;
57
+ --icon--color: #e0ead8;
58
+ --active--color: #5a9e4b;
59
+ --error--color: #e8836a;
60
+ --inactive--color: #526b4a;
61
+ --inactive--border-color: #3d5c3d;
62
+ --plugin--background: #1a2e1a;
63
+ --modal-target--background: rgba(224, 234, 216, 0.05);
64
+ --active--background: rgba(90, 158, 75, 0.5);
65
+ --expression--operator-color: #b8c9ad;
66
+ --expression--function-color: #7bc96f;
67
+ --expression--error-color: rgb(232, 131, 106);
68
+ --calendar--filter: invert(1);
69
+ --warning--color: #1a2e1a;
70
+ --warning--background: var(--icon--color);
71
+
72
+ --select-arrow--background-image: var(
73
+ --select-arrow-light--background-image
74
+ );
75
+
76
+ --select-arrow--hover--background-image: var(
77
+ --select-arrow-dark--background-image
78
+ );
79
+
80
+ // Syntax
81
+ --code-editor-symbol--color: #e0ead8;
82
+ --code-editor-literal--color: #a8d8a0;
83
+ --code-editor-operator--color: rgb(206, 176, 104);
84
+ --code-editor-comment--color: rgb(120, 160, 100);
85
+ --code-editor-column--color: #c9a0d8;
86
+ }
87
+
88
+ @mixin perspective-viewer-botanical--openlayers {
89
+ --map-tile-url: "http://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png";
90
+ --map-attribution--filter: invert(1) hue-rotate(180deg);
91
+ --map-element-background: #1e3420;
92
+ --map-category-1: rgb(90, 158, 75);
93
+ --map-category-2: rgb(206, 176, 104);
94
+ --map-category-3: rgb(160, 110, 180);
95
+ --map-category-4: rgb(80, 170, 150);
96
+ --map-category-5: rgb(140, 170, 90);
97
+ --map-category-6: rgb(200, 120, 140);
98
+ --map-category-7: rgb(100, 150, 190);
99
+ --map-category-8: rgb(210, 140, 80);
100
+ --map-category-9: rgb(130, 120, 190);
101
+ --map-category-10: rgb(170, 200, 110);
102
+ --map-gradient: linear-gradient(#e8836a 0%, #1a2e1a 50%, #5a9e4b 100%);
103
+ }
104
+
105
+ @mixin perspective-viewer-botanical--datagrid {
106
+ --rt-pos-cell--color: #7bc96f;
107
+ --rt-neg-cell--color: #ebac21;
108
+ }
109
+
110
+ @mixin perspective-viewer-botanical--d3fc {
111
+ --d3fc-legend--text: #b8c9ad;
112
+ --d3fc-treedata--labels: #e0ead8;
113
+ --d3fc-treedata--hover-highlight: #e0ead8;
114
+ --d3fc-tooltip--color: #e0ead8;
115
+ --d3fc-axis-ticks--color: #b8c9ad;
116
+ --d3fc-axis--lines: #526b4a;
117
+ --d3fc-gridline--color: #2a4228;
118
+ --d3fc-tooltip--background: rgba(30, 52, 32, 1);
119
+ --d3fc-tooltip--border-color: #1a2e1a;
120
+ --d3fc-legend--background: var(--plugin--background);
121
+
122
+ --d3fc-series: rgb(90, 158, 75);
123
+ --d3fc-series-1: rgb(90, 158, 75);
124
+ --d3fc-series-2: rgb(206, 176, 104);
125
+ --d3fc-series-3: rgb(160, 110, 180);
126
+ --d3fc-series-4: rgb(80, 170, 150);
127
+ --d3fc-series-5: rgb(140, 170, 90);
128
+ --d3fc-series-6: rgb(200, 120, 140);
129
+ --d3fc-series-7: rgb(100, 150, 190);
130
+ --d3fc-series-8: rgb(210, 140, 80);
131
+ --d3fc-series-9: rgb(130, 120, 190);
132
+ --d3fc-series-10: rgb(170, 200, 110);
133
+
134
+ --d3fc-full--gradient: linear-gradient(
135
+ #e8836a 0%,
136
+ #1a2e1a 50%,
137
+ #5a9e4b 100%
138
+ );
139
+
140
+ --d3fc-positive--gradient: linear-gradient(#1a2e1a 0%, #5a9e4b 100%);
141
+ --d3fc-negative--gradient: linear-gradient(#e8836a 0%, #1a2e1a 100%);
142
+ }
@@ -18,4 +18,5 @@
18
18
  @import "vaporwave.less";
19
19
  @import "gruvbox.less";
20
20
  @import "gruvbox-dark.less";
21
- @import "dracula.less";
21
+ @import "dracula.less";
22
+ @import "botanical.less";
@@ -14,7 +14,41 @@ import type { HTMLPerspectiveViewerPluginElement } from "./plugin";
14
14
  import type { PerspectiveViewerElement } from "../../dist/wasm/perspective-viewer.js";
15
15
  import type React from "react";
16
16
  import type { ViewerConfigUpdate } from "./ts-rs/ViewerConfigUpdate.js";
17
- import type { ViewWindow } from "@perspective-dev/client";
17
+ import type {
18
+ ViewWindow,
19
+ ViewConfigUpdate,
20
+ Filter,
21
+ } from "@perspective-dev/client";
22
+
23
+ export class PerspectiveSelectDetail {
24
+ selected: boolean;
25
+ row: Record<string, unknown>;
26
+ column_names?: string[];
27
+ removeConfigs: ViewConfigUpdate[];
28
+ insertConfigs: ViewConfigUpdate[];
29
+
30
+ constructor(
31
+ selected: boolean,
32
+ row: Record<string, unknown>,
33
+ column_names: string[],
34
+ removeConfigs: ViewConfigUpdate[],
35
+ insertConfigs: ViewConfigUpdate[],
36
+ ) {
37
+ this.selected = selected;
38
+ this.row = row;
39
+ this.column_names = column_names;
40
+ this.removeConfigs = removeConfigs;
41
+ this.insertConfigs = insertConfigs;
42
+ }
43
+
44
+ get removeFilters(): Filter[] {
45
+ return this.removeConfigs.flatMap((x) => x.filter ?? []);
46
+ }
47
+
48
+ get insertFilters(): Filter[] {
49
+ return this.insertConfigs.flatMap((x) => x.filter ?? []);
50
+ }
51
+ }
18
52
  import type {
19
53
  ExportDropDownMenuElement,
20
54
  CopyDropDownMenuElement,
@@ -48,11 +82,15 @@ export type PerspectiveSelectEventDetail = {
48
82
 
49
83
  type ReactPerspectiveViewerAttributes<T> = React.HTMLAttributes<T>;
50
84
 
51
- type JsxPerspectiveViewerElement = { class?: string } & React.DetailedHTMLProps<
85
+ type JsxPerspectiveViewerElement = {
86
+ class?: string;
87
+ } & React.DetailedHTMLProps<
52
88
  ReactPerspectiveViewerAttributes<HTMLPerspectiveViewerElement>,
53
89
  HTMLPerspectiveViewerElement
54
90
  >;
55
91
 
92
+ // React <19
93
+
56
94
  declare global {
57
95
  namespace JSX {
58
96
  interface IntrinsicElements {
@@ -61,6 +99,39 @@ declare global {
61
99
  }
62
100
  }
63
101
 
102
+ // React >=19
103
+
104
+ // Why are these `ts-ignore`? React 19 makes choice of JSX runtime in `tsconfig`
105
+ // the determination of which runtime this needs to be, but this is chosen
106
+ // by the user ... so I'm not sure what the React authors want from me here
107
+ // exactly. Divination?
108
+
109
+ // @ts-ignore
110
+ declare module "react/jsx-runtime" {
111
+ namespace JSX {
112
+ interface IntrinsicElements {
113
+ "perspective-viewer": JsxPerspectiveViewerElement;
114
+ }
115
+ }
116
+ }
117
+
118
+ // @ts-ignore
119
+ declare module "react/jsx-dev-runtime" {
120
+ namespace JSX {
121
+ interface IntrinsicElements {
122
+ "perspective-viewer": JsxPerspectiveViewerElement;
123
+ }
124
+ }
125
+ }
126
+
127
+ declare module "react" {
128
+ namespace JSX {
129
+ interface IntrinsicElements {
130
+ "perspective-viewer": JsxPerspectiveViewerElement;
131
+ }
132
+ }
133
+ }
134
+
64
135
  // Custom Elements extensions
65
136
 
66
137
  declare global {
@@ -35,6 +35,7 @@ export { IPerspectiveViewerPlugin } from "./plugin";
35
35
  export { HTMLPerspectiveViewerPluginElement } from "./plugin";
36
36
 
37
37
  export type * from "./extensions.ts";
38
+ export { PerspectiveSelectDetail } from "./extensions.ts";
38
39
  export type * from "./ts-rs/ViewerConfigUpdate.d.ts";
39
40
  export type * from "./ts-rs/ColumnConfigValues.d.ts";
40
41
  export type * from "./ts-rs/Filter.d.ts";
@@ -0,0 +1,3 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ export type GroupRollupMode = "rollup" | "flat" | "total";
@@ -4,6 +4,7 @@ import type { ColumnConfigValues } from "./ColumnConfigValues.js";
4
4
  import type { Expressions } from "./Expressions.js";
5
5
  import type { Filter } from "./Filter.js";
6
6
  import type { FilterReducer } from "./FilterReducer.js";
7
+ import type { GroupRollupMode } from "./GroupRollupMode.js";
7
8
  import type { OptionalUpdate } from "./OptionalUpdate.js";
8
9
  import type { PluginConfig } from "./PluginConfig.js";
9
10
  import type { Sort } from "./Sort.js";
@@ -87,4 +88,4 @@ expressions?: Expressions,
87
88
  * applied to columns in the `View` constructor using a dictionary of
88
89
  * column name to aggregate function name.
89
90
  */
90
- aggregates?: { [key in string]?: Aggregate }, group_by_depth?: number, filter_op?: FilterReducer, };
91
+ aggregates?: { [key in string]?: Aggregate }, group_by_depth?: number, filter_op?: FilterReducer, group_rollup_mode?: GroupRollupMode, };
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "jsx": "preserve",
3
4
  "strict": true,
4
5
  "module": "ESNext",
5
6
  "target": "ESNext",