@perspective-dev/viewer-datagrid 4.4.0 → 4.5.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-datagrid.js +4 -22
- package/dist/cdn/perspective-viewer-datagrid.js.map +4 -4
- package/dist/css/perspective-viewer-datagrid-toolbar.css +1 -1
- package/dist/css/perspective-viewer-datagrid.css +1 -1
- package/dist/esm/color_utils.d.ts +22 -0
- package/dist/esm/custom_elements/datagrid.d.ts +16 -21
- package/dist/esm/data_listener/format_cell.d.ts +1 -1
- package/dist/esm/data_listener/formatter_cache.d.ts +1 -1
- package/dist/esm/data_listener/index.d.ts +3 -2
- package/dist/esm/event_handlers/click/edit_click.d.ts +3 -2
- package/dist/esm/event_handlers/click.d.ts +4 -6
- package/dist/esm/event_handlers/dispatch_click.d.ts +3 -2
- package/dist/esm/event_handlers/expand_collapse.d.ts +1 -1
- package/dist/esm/event_handlers/focus.d.ts +4 -5
- package/dist/esm/event_handlers/header_click.d.ts +5 -3
- package/dist/esm/event_handlers/keydown/edit_keydown.d.ts +3 -4
- package/dist/esm/event_handlers/select_region.d.ts +3 -1
- package/dist/esm/event_handlers/sort.d.ts +8 -7
- package/dist/esm/model/create.d.ts +1 -1
- package/dist/esm/model/meta_columns.d.ts +1 -0
- package/dist/esm/perspective-viewer-datagrid.js +3 -3
- package/dist/esm/perspective-viewer-datagrid.js.map +4 -4
- package/dist/esm/plugin/activate.d.ts +1 -1
- package/dist/esm/plugin/column_config_schema.d.ts +31 -0
- package/dist/esm/style_handlers/body.d.ts +3 -3
- package/dist/esm/style_handlers/column_header.d.ts +4 -3
- package/dist/esm/style_handlers/consolidated.d.ts +3 -47
- package/dist/esm/style_handlers/editable.d.ts +3 -2
- package/dist/esm/style_handlers/focus.d.ts +4 -4
- package/dist/esm/style_handlers/group_header.d.ts +1 -1
- package/dist/esm/style_handlers/table_cell/boolean.d.ts +1 -1
- package/dist/esm/style_handlers/table_cell/cell_flash.d.ts +1 -1
- package/dist/esm/style_handlers/table_cell/datetime.d.ts +1 -1
- package/dist/esm/style_handlers/table_cell/numeric.d.ts +1 -1
- package/dist/esm/style_handlers/table_cell/row_header.d.ts +1 -1
- package/dist/esm/style_handlers/table_cell/string.d.ts +1 -1
- package/dist/esm/style_handlers/types.d.ts +0 -4
- package/dist/esm/types.d.ts +10 -17
- package/package.json +2 -4
- package/src/css/regular_table.css +87 -31
- package/src/css/row-hover.css +20 -7
- package/src/css/toolbar.css +11 -0
- package/src/ts/color_utils.ts +181 -16
- package/src/ts/custom_elements/datagrid.ts +70 -56
- package/src/ts/custom_elements/toolbar.ts +4 -5
- package/src/ts/data_listener/format_cell.ts +28 -9
- package/src/ts/data_listener/format_tree_header.ts +2 -2
- package/src/ts/data_listener/formatter_cache.ts +9 -96
- package/src/ts/data_listener/index.ts +13 -11
- package/src/ts/event_handlers/click/edit_click.ts +10 -6
- package/src/ts/event_handlers/click.ts +39 -68
- package/src/ts/event_handlers/dispatch_click.ts +27 -25
- package/src/ts/event_handlers/expand_collapse.ts +11 -8
- package/src/ts/event_handlers/focus.ts +38 -35
- package/src/ts/event_handlers/header_click.ts +107 -62
- package/src/ts/event_handlers/keydown/edit_keydown.ts +60 -54
- package/src/ts/event_handlers/select_region.ts +153 -131
- package/src/ts/event_handlers/sort.ts +20 -25
- package/src/ts/get_cell_config.ts +10 -3
- package/src/ts/model/column_overrides.ts +16 -9
- package/src/ts/model/create.ts +68 -55
- package/src/ts/{event_handlers/deselect_all.ts → model/meta_columns.ts} +33 -14
- package/src/ts/model/toolbar.ts +33 -8
- package/src/ts/plugin/activate.ts +122 -92
- package/src/ts/plugin/column_config_schema.ts +187 -0
- package/src/ts/plugin/draw.ts +1 -0
- package/src/ts/plugin/restore.ts +6 -2
- package/src/ts/plugin/save.ts +2 -5
- package/src/ts/style_handlers/body.ts +48 -51
- package/src/ts/style_handlers/column_header.ts +22 -21
- package/src/ts/style_handlers/consolidated.ts +23 -123
- package/src/ts/style_handlers/editable.ts +16 -10
- package/src/ts/style_handlers/focus.ts +7 -5
- package/src/ts/style_handlers/group_header.ts +13 -6
- package/src/ts/style_handlers/table_cell/boolean.ts +3 -3
- package/src/ts/style_handlers/table_cell/cell_flash.ts +11 -11
- package/src/ts/style_handlers/table_cell/datetime.ts +3 -3
- package/src/ts/style_handlers/table_cell/numeric.ts +24 -25
- package/src/ts/style_handlers/table_cell/row_header.ts +2 -2
- package/src/ts/style_handlers/table_cell/string.ts +20 -18
- package/src/ts/style_handlers/types.ts +0 -10
- package/src/ts/types.ts +28 -20
- package/dist/esm/event_handlers/deselect_all.d.ts +0 -5
- package/dist/esm/event_handlers/row_select_click.d.ts +0 -4
- package/dist/esm/plugin/column_style_controls.d.ts +0 -28
- package/src/ts/event_handlers/row_select_click.ts +0 -92
- package/src/ts/plugin/column_style_controls.ts +0 -76
|
@@ -10,14 +10,10 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
import { RegularTableElement } from "regular-table";
|
|
14
|
-
import type {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
SortRotationOrder,
|
|
18
|
-
SortTerm,
|
|
19
|
-
} from "../types.js";
|
|
20
|
-
import { SortDir } from "@perspective-dev/client";
|
|
13
|
+
import type { RegularTableElement } from "regular-table";
|
|
14
|
+
import type { DatagridModel, SortRotationOrder, SortTerm } from "../types.js";
|
|
15
|
+
import type { SortDir } from "@perspective-dev/client";
|
|
16
|
+
import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
|
|
21
17
|
|
|
22
18
|
const ROW_SORT_ORDER: SortRotationOrder = {
|
|
23
19
|
desc: "asc",
|
|
@@ -31,22 +27,21 @@ const ROW_COL_SORT_ORDER: SortRotationOrder = {
|
|
|
31
27
|
asc: undefined,
|
|
32
28
|
"desc abs": "asc abs",
|
|
33
29
|
"asc abs": undefined,
|
|
34
|
-
// "col desc": "col asc",
|
|
35
|
-
// "col asc": undefined,
|
|
36
|
-
// "col desc abs": "col asc abs",
|
|
37
|
-
// "col asc abs": undefined,
|
|
38
30
|
};
|
|
39
31
|
|
|
40
32
|
export async function sortHandler(
|
|
41
|
-
|
|
33
|
+
model: DatagridModel,
|
|
42
34
|
regularTable: RegularTableElement,
|
|
43
|
-
viewer:
|
|
35
|
+
viewer: HTMLPerspectiveViewerElement,
|
|
44
36
|
event: MouseEvent,
|
|
45
37
|
target: HTMLElement,
|
|
46
38
|
): Promise<void> {
|
|
47
39
|
const meta = regularTable.getMeta(target);
|
|
48
|
-
if (!meta?.column_header)
|
|
49
|
-
|
|
40
|
+
if (!meta?.column_header) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const column_name = meta.column_header[model._config.split_by.length];
|
|
50
45
|
const sort_method =
|
|
51
46
|
event.ctrlKey ||
|
|
52
47
|
(event as MouseEvent & { metaKey?: boolean }).metaKey ||
|
|
@@ -55,22 +50,22 @@ export async function sortHandler(
|
|
|
55
50
|
: override_sort;
|
|
56
51
|
|
|
57
52
|
const abs = event.shiftKey;
|
|
58
|
-
const sort = sort_method
|
|
53
|
+
const sort = sort_method(model, `${column_name}`, abs);
|
|
59
54
|
await viewer.restore({ sort });
|
|
60
55
|
}
|
|
61
56
|
|
|
62
57
|
export function append_sort(
|
|
63
|
-
|
|
58
|
+
model: DatagridModel,
|
|
64
59
|
column_name: string,
|
|
65
60
|
abs: boolean,
|
|
66
61
|
): SortTerm[] {
|
|
67
62
|
const sort: SortTerm[] = [];
|
|
68
63
|
let found = false;
|
|
69
|
-
for (const sort_term of
|
|
64
|
+
for (const sort_term of model._config.sort) {
|
|
70
65
|
const [_column_name, _sort_dir] = sort_term;
|
|
71
66
|
if (_column_name === column_name) {
|
|
72
67
|
found = true;
|
|
73
|
-
const term = create_sort
|
|
68
|
+
const term = create_sort(model, column_name, _sort_dir, abs);
|
|
74
69
|
if (term) {
|
|
75
70
|
sort.push(term);
|
|
76
71
|
}
|
|
@@ -87,13 +82,13 @@ export function append_sort(
|
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
export function override_sort(
|
|
90
|
-
|
|
85
|
+
model: DatagridModel,
|
|
91
86
|
column_name: string,
|
|
92
87
|
abs: boolean,
|
|
93
88
|
): SortTerm[] {
|
|
94
|
-
for (const [_column_name, _sort_dir] of
|
|
89
|
+
for (const [_column_name, _sort_dir] of model._config.sort) {
|
|
95
90
|
if (_column_name === column_name) {
|
|
96
|
-
const sort = create_sort
|
|
91
|
+
const sort = create_sort(model, column_name, _sort_dir, abs);
|
|
97
92
|
return sort ? [sort] : [];
|
|
98
93
|
}
|
|
99
94
|
}
|
|
@@ -102,12 +97,12 @@ export function override_sort(
|
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
export function create_sort(
|
|
105
|
-
|
|
100
|
+
model: DatagridModel,
|
|
106
101
|
column_name: string,
|
|
107
102
|
sort_dir: SortDir | undefined,
|
|
108
103
|
_abs: boolean,
|
|
109
104
|
): SortTerm | undefined {
|
|
110
|
-
const is_col_sortable =
|
|
105
|
+
const is_col_sortable = model._config.split_by.length > 0;
|
|
111
106
|
const order = is_col_sortable ? ROW_COL_SORT_ORDER : ROW_SORT_ORDER;
|
|
112
107
|
const inc_sort_dir: SortDir | undefined = sort_dir
|
|
113
108
|
? order[sort_dir]
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import type { View, ViewConfig, Filter, Scalar } from "@perspective-dev/client";
|
|
14
14
|
import type { CellConfigResult } from "./types.js";
|
|
15
|
+
import { isMetaColumn } from "./model/meta_columns.js";
|
|
15
16
|
|
|
16
17
|
interface ModelWithViewAndConfig {
|
|
17
18
|
_view: View;
|
|
@@ -42,8 +43,14 @@ export default async function getCellConfig(
|
|
|
42
43
|
})
|
|
43
44
|
.filter((x): x is Filter => x !== undefined);
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// Filter out *all* meta columns before indexing into the row's
|
|
47
|
+
// keys — the DuckDB virtual server's JSON output now includes
|
|
48
|
+
// per-level `__ROW_PATH_<n>__` columns alongside the
|
|
49
|
+
// `__ROW_PATH__` sidecar, so the previous `+1` skip (which
|
|
50
|
+
// assumed exactly one leading meta column) lands on a meta key
|
|
51
|
+
// when group_by has multiple levels.
|
|
52
|
+
const user_keys = Object.keys(r[0]).filter((k) => !isMetaColumn(k));
|
|
53
|
+
const column_paths = user_keys[col_idx];
|
|
47
54
|
const result: CellConfigResult = {
|
|
48
55
|
row: r[0] as Record<string, unknown>,
|
|
49
56
|
column_names: [],
|
|
@@ -60,7 +67,7 @@ export default async function getCellConfig(
|
|
|
60
67
|
return pivot_value ? [pivot, "==", pivot_value] : undefined;
|
|
61
68
|
})
|
|
62
69
|
.filter((x): x is Filter => x !== undefined)
|
|
63
|
-
.filter(([, , value]) => value
|
|
70
|
+
.filter(([, , value]) => !isMetaColumn(value as string));
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
const filter = _config.filter.concat(row_filters).concat(column_filters);
|
|
@@ -10,11 +10,7 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
import type {
|
|
14
|
-
ColumnOverrides,
|
|
15
|
-
DatagridPluginElement,
|
|
16
|
-
RegularTable,
|
|
17
|
-
} from "../types.js";
|
|
13
|
+
import type { ColumnOverrides, DatagridPluginElement } from "../types.js";
|
|
18
14
|
|
|
19
15
|
interface RegularTableWithOverrides {
|
|
20
16
|
restoreColumnSizes(overrides: Record<number, number | undefined>): void;
|
|
@@ -46,7 +42,10 @@ export function restore_column_size_overrides(
|
|
|
46
42
|
this._cached_column_sizes = old_sizes;
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
const
|
|
45
|
+
const regular_table = this.regular_table as RegularTableWithOverrides;
|
|
46
|
+
const overrides: Record<number, number | undefined> = {
|
|
47
|
+
...regular_table.saveColumnSizes(),
|
|
48
|
+
};
|
|
50
49
|
const { group_by } = this.model!._config;
|
|
51
50
|
const tree_header_offset = group_by?.length > 0 ? group_by.length + 1 : 0;
|
|
52
51
|
|
|
@@ -57,15 +56,23 @@ export function restore_column_size_overrides(
|
|
|
57
56
|
| undefined;
|
|
58
57
|
} else {
|
|
59
58
|
const index = this.model!._column_paths.indexOf(key);
|
|
59
|
+
|
|
60
|
+
// Skip keys that don't resolve to a known column — e.g. on the
|
|
61
|
+
// first draw after `activate`, `_column_paths` has not yet been
|
|
62
|
+
// populated by the data listener, so we leave any existing
|
|
63
|
+
// `regular-table` widths untouched rather than clobbering them
|
|
64
|
+
// with garbage indices.
|
|
65
|
+
if (index === -1) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
overrides[index + tree_header_offset] = old_sizes[key] as
|
|
61
70
|
| number
|
|
62
71
|
| undefined;
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
|
|
67
|
-
overrides,
|
|
68
|
-
);
|
|
75
|
+
regular_table.restoreColumnSizes(overrides);
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
/**
|
package/src/ts/model/create.ts
CHANGED
|
@@ -10,9 +10,8 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
import chroma from "chroma-js";
|
|
14
13
|
import { createDataListener } from "../data_listener/index.js";
|
|
15
|
-
import { blend, make_color_record } from "../color_utils.js";
|
|
14
|
+
import { blend, make_color_record, parseColor } from "../color_utils.js";
|
|
16
15
|
import type {
|
|
17
16
|
ColumnType,
|
|
18
17
|
Table,
|
|
@@ -26,10 +25,42 @@ import {
|
|
|
26
25
|
type Schema,
|
|
27
26
|
type ElemFactory,
|
|
28
27
|
type EditMode,
|
|
29
|
-
type PerspectiveViewerElement,
|
|
30
|
-
get_psp_type,
|
|
31
28
|
} from "../types.js";
|
|
32
|
-
import {
|
|
29
|
+
import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
|
|
30
|
+
|
|
31
|
+
function arraysChanged<T>(a: T[], b: T[]): boolean {
|
|
32
|
+
if (a.length !== b.length) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < a.length; i++) {
|
|
37
|
+
if (a[i] !== b[i]) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function nestedArraysChanged<T>(a: T[][], b: T[][]): boolean {
|
|
46
|
+
if (a.length !== b.length) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < a.length; i++) {
|
|
51
|
+
if (a[i].length !== b[i].length) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (let j = 0; j < a[i].length; j++) {
|
|
56
|
+
if (a[i][j] !== b[i][j]) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
33
64
|
|
|
34
65
|
function get_rule(regular: HTMLElement, tag: string, def: string): string {
|
|
35
66
|
const color = window.getComputedStyle(regular).getPropertyValue(tag).trim();
|
|
@@ -59,6 +90,7 @@ class ElemFactoryImpl implements ElemFactory {
|
|
|
59
90
|
if (!this._elements[this._index]) {
|
|
60
91
|
this._elements[this._index] = document.createElement(this._name);
|
|
61
92
|
}
|
|
93
|
+
|
|
62
94
|
const elem = this._elements[this._index];
|
|
63
95
|
this._index += 1;
|
|
64
96
|
return elem;
|
|
@@ -70,61 +102,33 @@ export async function createModel(
|
|
|
70
102
|
regular: RegularTable,
|
|
71
103
|
table: Table,
|
|
72
104
|
view: View,
|
|
105
|
+
theme: string,
|
|
73
106
|
extend: Partial<DatagridModel> = {},
|
|
74
107
|
): Promise<DatagridModel> {
|
|
75
108
|
const config = (await view.get_config()) as ViewConfig;
|
|
76
109
|
if (this?.model?._config) {
|
|
77
110
|
const old = this.model._config;
|
|
78
|
-
|
|
111
|
+
const group_by_changed = arraysChanged(old.group_by, config.group_by);
|
|
79
112
|
const type_changed =
|
|
80
113
|
(old.group_by.length === 0 || config.group_by.length === 0) &&
|
|
81
114
|
group_by_changed;
|
|
82
115
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
let split_by_changed = old.split_by.length !== config.split_by.length;
|
|
90
|
-
if (!split_by_changed) {
|
|
91
|
-
for (const lvl in old.split_by) {
|
|
92
|
-
split_by_changed ||= config.split_by[lvl] !== old.split_by[lvl];
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
let columns_changed = old.columns.length !== config.columns.length;
|
|
97
|
-
if (!columns_changed) {
|
|
98
|
-
for (const lvl in old.columns) {
|
|
99
|
-
columns_changed ||= config.columns[lvl] !== old.columns[lvl];
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let filter_changed = old.filter.length !== config.filter.length;
|
|
104
|
-
if (!filter_changed) {
|
|
105
|
-
for (const lvl in old.filter) {
|
|
106
|
-
for (const i in config.filter[lvl]) {
|
|
107
|
-
filter_changed ||=
|
|
108
|
-
config.filter[lvl][i as unknown as number] !==
|
|
109
|
-
old.filter[lvl][i as unknown as number];
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
116
|
+
const split_by_changed = arraysChanged(old.split_by, config.split_by);
|
|
117
|
+
const columns_changed = arraysChanged(old.columns, config.columns);
|
|
118
|
+
const filter_changed = nestedArraysChanged(
|
|
119
|
+
old.filter as unknown[][],
|
|
120
|
+
config.filter as unknown[][],
|
|
121
|
+
);
|
|
113
122
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
sort_changed ||=
|
|
119
|
-
config.sort[lvl][i as unknown as number] !==
|
|
120
|
-
old.sort[lvl][i as unknown as number];
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
123
|
+
const sort_changed = nestedArraysChanged(
|
|
124
|
+
old.sort as unknown[][],
|
|
125
|
+
config.sort as unknown[][],
|
|
126
|
+
);
|
|
124
127
|
|
|
125
128
|
const group_rollup_mode_changed =
|
|
126
129
|
old.group_rollup_mode !== config.group_rollup_mode;
|
|
127
130
|
|
|
131
|
+
const theme_changed = this.model._theme !== theme;
|
|
128
132
|
this._reset_scroll_top = group_by_changed;
|
|
129
133
|
this._reset_scroll_left = split_by_changed;
|
|
130
134
|
this._reset_select =
|
|
@@ -139,6 +143,7 @@ export async function createModel(
|
|
|
139
143
|
split_by_changed ||
|
|
140
144
|
group_by_changed ||
|
|
141
145
|
columns_changed ||
|
|
146
|
+
theme_changed ||
|
|
142
147
|
type_changed;
|
|
143
148
|
}
|
|
144
149
|
|
|
@@ -148,12 +153,12 @@ export async function createModel(
|
|
|
148
153
|
view.num_rows(),
|
|
149
154
|
view.schema(),
|
|
150
155
|
view.expression_schema(),
|
|
151
|
-
(this.parentElement as
|
|
156
|
+
(this.parentElement as HTMLPerspectiveViewerElement).getEditPort(),
|
|
152
157
|
]);
|
|
153
158
|
|
|
154
|
-
const _plugin_background =
|
|
159
|
+
const _plugin_background = parseColor(
|
|
155
160
|
get_rule(regular, "--psp--background-color", "#FFFFFF"),
|
|
156
|
-
)
|
|
161
|
+
);
|
|
157
162
|
|
|
158
163
|
const _pos_fg_color = make_color_record(
|
|
159
164
|
get_rule(regular, "--psp-datagrid--pos-cell--color", "#338DCD"),
|
|
@@ -187,10 +192,17 @@ export async function createModel(
|
|
|
187
192
|
const _column_paths: string[] = [];
|
|
188
193
|
const _is_editable: boolean[] = [];
|
|
189
194
|
const _column_types: ColumnType[] = [];
|
|
195
|
+
let _edit_mode: EditMode = this._edit_mode || "READ_ONLY";
|
|
190
196
|
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
if (
|
|
198
|
+
_edit_mode === "SELECT_ROW_TREE" &&
|
|
199
|
+
(config.group_by.length === 0 || config.group_rollup_mode === "flat")
|
|
200
|
+
) {
|
|
201
|
+
_edit_mode = "READ_ONLY";
|
|
202
|
+
this._edit_mode = _edit_mode;
|
|
203
|
+
}
|
|
193
204
|
|
|
205
|
+
this._edit_button!.dataset.editMode = _edit_mode;
|
|
194
206
|
const model: DatagridModel = Object.assign(extend, {
|
|
195
207
|
_edit_port,
|
|
196
208
|
_view: view,
|
|
@@ -208,6 +220,7 @@ export async function createModel(
|
|
|
208
220
|
_neg_bg_color,
|
|
209
221
|
_column_paths,
|
|
210
222
|
_column_types,
|
|
223
|
+
_theme: theme,
|
|
211
224
|
_is_editable,
|
|
212
225
|
_edit_mode,
|
|
213
226
|
_selection_state: {
|
|
@@ -219,15 +232,15 @@ export async function createModel(
|
|
|
219
232
|
}),
|
|
220
233
|
_series_color_map: new Map<string, string>(),
|
|
221
234
|
_series_color_seed: new Map<string, number>(),
|
|
235
|
+
|
|
222
236
|
// get_psp_type,
|
|
223
237
|
_div_factory: extend._div_factory || new ElemFactoryImpl("div"),
|
|
224
238
|
}) as DatagridModel;
|
|
225
239
|
|
|
226
240
|
regular.setDataListener(
|
|
227
|
-
createDataListener(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
) as any,
|
|
241
|
+
createDataListener(
|
|
242
|
+
this.parentElement as HTMLPerspectiveViewerElement,
|
|
243
|
+
).bind(model, regular) as any,
|
|
231
244
|
{
|
|
232
245
|
virtual_mode: (window
|
|
233
246
|
.getComputedStyle(regular)
|
|
@@ -10,19 +10,38 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Single-source-of-truth predicate for "this column is metadata, hide
|
|
15
|
+
* it from the user-visible grid." Five distinct names show up across
|
|
16
|
+
* the perspective protocol; older datagrid code filtered them by
|
|
17
|
+
* exact-match in three places, which only worked when the wire shape
|
|
18
|
+
* was the JSON-sidecar form (`__ROW_PATH__` array-of-arrays).
|
|
19
|
+
*
|
|
20
|
+
* The DuckDB virtual-server backend additionally surfaces per-level
|
|
21
|
+
* `__ROW_PATH_<n>__` columns directly in `to_columns_string` /
|
|
22
|
+
* `to_json` output (a side effect of keeping them in the frozen Arrow
|
|
23
|
+
* batch so that `to_arrow` consumers — viewer-charts via
|
|
24
|
+
* `with_typed_arrays` — see them inline, matching native
|
|
25
|
+
* `perspective-server`'s `to_arrow` behavior). Without this helper,
|
|
26
|
+
* those per-level columns slip through the legacy exact-match filters
|
|
27
|
+
* and render as user columns to the right of the grid.
|
|
28
|
+
*
|
|
29
|
+
* Match list:
|
|
30
|
+
* - `__ROW_PATH__` — JSON sidecar; used by the tree header.
|
|
31
|
+
* - `__ROW_PATH_<n>__` — per-level columns from the virtual server's
|
|
32
|
+
* inline-arrow shape.
|
|
33
|
+
* - `__ID__` — per-row identity column.
|
|
34
|
+
* - `__GROUPING_ID__` — internal SQL-rollup discriminator. The
|
|
35
|
+
* virtual server strips it server-side, but
|
|
36
|
+
* we cover it defensively in case a future
|
|
37
|
+
* backend leaks it.
|
|
38
|
+
*
|
|
39
|
+
* User columns named with leading/trailing double-underscores (e.g.
|
|
40
|
+
* `__user_col__`) are *not* matched — the regex requires the exact
|
|
41
|
+
* stems above.
|
|
42
|
+
*/
|
|
43
|
+
const META_COLUMN_RE = /^__(?:ROW_PATH(?:_\d+)?|ID|GROUPING_ID)__$/;
|
|
15
44
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export async function deselect_all_listener(
|
|
19
|
-
regularTable: RegularTableElement,
|
|
20
|
-
_viewer: PerspectiveViewerElement,
|
|
21
|
-
selected_rows_map: SelectedRowsMap,
|
|
22
|
-
): Promise<void> {
|
|
23
|
-
selected_rows_map.delete(regularTable);
|
|
24
|
-
for (const td of regularTable.querySelectorAll("td,th")) {
|
|
25
|
-
td.classList.toggle("psp-row-selected", false);
|
|
26
|
-
td.classList.toggle("psp-row-subselected", false);
|
|
27
|
-
}
|
|
45
|
+
export function isMetaColumn(name: string): boolean {
|
|
46
|
+
return META_COLUMN_RE.test(name);
|
|
28
47
|
}
|
package/src/ts/model/toolbar.ts
CHANGED
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
|
|
14
|
+
import type {
|
|
15
|
+
DatagridModel,
|
|
16
|
+
DatagridPluginElement,
|
|
17
|
+
EditMode,
|
|
18
|
+
} from "../types.js";
|
|
14
19
|
|
|
15
20
|
export const EDIT_MODES: readonly EditMode[] = [
|
|
16
21
|
"READ_ONLY",
|
|
@@ -18,34 +23,54 @@ export const EDIT_MODES: readonly EditMode[] = [
|
|
|
18
23
|
"SELECT_ROW",
|
|
19
24
|
"SELECT_COLUMN",
|
|
20
25
|
"SELECT_REGION",
|
|
26
|
+
"SELECT_ROW_TREE",
|
|
21
27
|
] as const;
|
|
22
28
|
|
|
29
|
+
function isSelectRowTreeAvailable(model?: DatagridModel): boolean {
|
|
30
|
+
if (!model) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
model._config.group_by.length > 0 &&
|
|
36
|
+
model._config.group_rollup_mode !== "flat"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
export function toggle_edit_mode(
|
|
24
41
|
this: DatagridPluginElement,
|
|
25
42
|
mode?: EditMode,
|
|
26
43
|
): void {
|
|
27
44
|
if (typeof mode === "undefined") {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
45
|
+
let idx = EDIT_MODES.indexOf(this._edit_mode);
|
|
46
|
+
do {
|
|
47
|
+
idx = (idx + 1) % EDIT_MODES.length;
|
|
48
|
+
} while (
|
|
49
|
+
EDIT_MODES[idx] === "SELECT_ROW_TREE" &&
|
|
50
|
+
!isSelectRowTreeAvailable(this.model)
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
mode = EDIT_MODES[idx];
|
|
32
54
|
}
|
|
33
55
|
|
|
34
|
-
(this.parentElement as
|
|
56
|
+
(this.parentElement as HTMLPerspectiveViewerElement)?.setSelection?.();
|
|
35
57
|
this._edit_mode = mode;
|
|
36
58
|
if (this.model) {
|
|
37
59
|
this.model._edit_mode = mode;
|
|
60
|
+
this.model._tree_selection_id = undefined;
|
|
38
61
|
this.model._selection_state = {
|
|
39
62
|
selected_areas: [],
|
|
40
63
|
dirty: true,
|
|
41
64
|
};
|
|
42
65
|
}
|
|
43
66
|
|
|
67
|
+
(this.parentElement as HTMLPerspectiveViewerElement)?.restore?.({
|
|
68
|
+
plugin_config: { edit_mode: mode },
|
|
69
|
+
});
|
|
70
|
+
|
|
44
71
|
if (this._edit_button !== undefined) {
|
|
45
72
|
this._edit_button.dataset.editMode = mode;
|
|
46
73
|
}
|
|
47
|
-
|
|
48
|
-
this.dataset.editMode = mode;
|
|
49
74
|
}
|
|
50
75
|
|
|
51
76
|
export function toggle_scroll_lock(
|