@perspective-dev/viewer-datagrid 4.4.1 → 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 -4
- package/dist/cdn/perspective-viewer-datagrid.js.map +4 -4
- package/dist/esm/custom_elements/datagrid.d.ts +12 -17
- 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/column_config_schema.d.ts +31 -0
- package/package.json +1 -1
- package/src/ts/color_utils.ts +50 -13
- package/src/ts/custom_elements/datagrid.ts +63 -48
- package/src/ts/data_listener/format_tree_header.ts +2 -2
- package/src/ts/data_listener/formatter_cache.ts +8 -95
- package/src/ts/data_listener/index.ts +9 -3
- package/src/ts/event_handlers/click/edit_click.ts +3 -0
- package/src/ts/event_handlers/dispatch_click.ts +4 -1
- package/src/ts/event_handlers/expand_collapse.ts +4 -1
- package/src/ts/event_handlers/header_click.ts +9 -3
- package/src/ts/event_handlers/keydown/edit_keydown.ts +11 -2
- package/src/ts/event_handlers/select_region.ts +15 -4
- package/src/ts/event_handlers/sort.ts +4 -1
- package/src/ts/get_cell_config.ts +10 -3
- package/src/ts/model/column_overrides.ts +3 -5
- package/src/ts/model/create.ts +22 -5
- package/src/ts/{plugin/column_style_controls.ts → model/meta_columns.ts} +33 -62
- package/src/ts/model/toolbar.ts +11 -2
- package/src/ts/plugin/activate.ts +3 -1
- 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 +1 -5
- package/src/ts/style_handlers/body.ts +6 -2
- package/src/ts/style_handlers/column_header.ts +6 -2
- package/src/ts/style_handlers/consolidated.ts +1 -0
- package/src/ts/style_handlers/editable.ts +6 -2
- package/src/ts/style_handlers/focus.ts +2 -0
- package/src/ts/style_handlers/group_header.ts +10 -4
- package/dist/esm/plugin/column_style_controls.d.ts +0 -28
|
@@ -37,7 +37,10 @@ export async function sortHandler(
|
|
|
37
37
|
target: HTMLElement,
|
|
38
38
|
): Promise<void> {
|
|
39
39
|
const meta = regularTable.getMeta(target);
|
|
40
|
-
if (!meta?.column_header)
|
|
40
|
+
if (!meta?.column_header) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
41
44
|
const column_name = meta.column_header[model._config.split_by.length];
|
|
42
45
|
const sort_method =
|
|
43
46
|
event.ctrlKey ||
|
|
@@ -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;
|
|
@@ -60,6 +56,7 @@ export function restore_column_size_overrides(
|
|
|
60
56
|
| undefined;
|
|
61
57
|
} else {
|
|
62
58
|
const index = this.model!._column_paths.indexOf(key);
|
|
59
|
+
|
|
63
60
|
// Skip keys that don't resolve to a known column — e.g. on the
|
|
64
61
|
// first draw after `activate`, `_column_paths` has not yet been
|
|
65
62
|
// populated by the data listener, so we leave any existing
|
|
@@ -68,6 +65,7 @@ export function restore_column_size_overrides(
|
|
|
68
65
|
if (index === -1) {
|
|
69
66
|
continue;
|
|
70
67
|
}
|
|
68
|
+
|
|
71
69
|
overrides[index + tree_header_offset] = old_sizes[key] as
|
|
72
70
|
| number
|
|
73
71
|
| undefined;
|
package/src/ts/model/create.ts
CHANGED
|
@@ -29,21 +29,36 @@ import {
|
|
|
29
29
|
import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
|
|
30
30
|
|
|
31
31
|
function arraysChanged<T>(a: T[], b: T[]): boolean {
|
|
32
|
-
if (a.length !== b.length)
|
|
32
|
+
if (a.length !== b.length) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
33
36
|
for (let i = 0; i < a.length; i++) {
|
|
34
|
-
if (a[i] !== b[i])
|
|
37
|
+
if (a[i] !== b[i]) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
35
40
|
}
|
|
41
|
+
|
|
36
42
|
return false;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
function nestedArraysChanged<T>(a: T[][], b: T[][]): boolean {
|
|
40
|
-
if (a.length !== b.length)
|
|
46
|
+
if (a.length !== b.length) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
for (let i = 0; i < a.length; i++) {
|
|
42
|
-
if (a[i].length !== b[i].length)
|
|
51
|
+
if (a[i].length !== b[i].length) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
for (let j = 0; j < a[i].length; j++) {
|
|
44
|
-
if (a[i][j] !== b[i][j])
|
|
56
|
+
if (a[i][j] !== b[i][j]) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
}
|
|
61
|
+
|
|
47
62
|
return false;
|
|
48
63
|
}
|
|
49
64
|
|
|
@@ -75,6 +90,7 @@ class ElemFactoryImpl implements ElemFactory {
|
|
|
75
90
|
if (!this._elements[this._index]) {
|
|
76
91
|
this._elements[this._index] = document.createElement(this._name);
|
|
77
92
|
}
|
|
93
|
+
|
|
78
94
|
const elem = this._elements[this._index];
|
|
79
95
|
this._index += 1;
|
|
80
96
|
return elem;
|
|
@@ -216,6 +232,7 @@ export async function createModel(
|
|
|
216
232
|
}),
|
|
217
233
|
_series_color_map: new Map<string, string>(),
|
|
218
234
|
_series_color_seed: new Map<string, number>(),
|
|
235
|
+
|
|
219
236
|
// get_psp_type,
|
|
220
237
|
_div_factory: extend._div_factory || new ElemFactoryImpl("div"),
|
|
221
238
|
}) as DatagridModel;
|
|
@@ -10,67 +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
|
-
fg_gradient: number;
|
|
19
|
-
pos_fg_color: string;
|
|
20
|
-
neg_fg_color: string;
|
|
21
|
-
number_fg_mode: string;
|
|
22
|
-
bg_gradient: number;
|
|
23
|
-
pos_bg_color: string;
|
|
24
|
-
neg_bg_color: string;
|
|
25
|
-
number_bg_mode: string;
|
|
26
|
-
};
|
|
27
|
-
number_string_format: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface DatetimeStyleOpts {
|
|
31
|
-
datagrid_datetime_style?: {
|
|
32
|
-
color: string;
|
|
33
|
-
bg_color: string;
|
|
34
|
-
};
|
|
35
|
-
datagrid_string_style?: {
|
|
36
|
-
color: string;
|
|
37
|
-
bg_color: string;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type ColumnStyleOpts = NumberStyleOpts | DatetimeStyleOpts | null;
|
|
42
|
-
|
|
43
|
-
export default function column_style_opts(
|
|
44
|
-
this: DatagridPluginElement,
|
|
45
|
-
type: ColumnType,
|
|
46
|
-
_group: string,
|
|
47
|
-
): ColumnStyleOpts {
|
|
48
|
-
if (type === "integer" || type === "float") {
|
|
49
|
-
return {
|
|
50
|
-
datagrid_number_style: {
|
|
51
|
-
fg_gradient: 0,
|
|
52
|
-
pos_fg_color: this.model!._pos_fg_color[0],
|
|
53
|
-
neg_fg_color: this.model!._neg_fg_color[0],
|
|
54
|
-
number_fg_mode: "color",
|
|
55
|
-
bg_gradient: 0,
|
|
56
|
-
pos_bg_color: this.model!._pos_bg_color[0],
|
|
57
|
-
neg_bg_color: this.model!._neg_bg_color[0],
|
|
58
|
-
number_bg_mode: "disabled",
|
|
59
|
-
},
|
|
60
|
-
number_string_format: true,
|
|
61
|
-
};
|
|
62
|
-
} else if (type === "date" || type === "datetime" || type === "string") {
|
|
63
|
-
const control =
|
|
64
|
-
type === "date" || type === "datetime"
|
|
65
|
-
? "datagrid_datetime_style"
|
|
66
|
-
: `datagrid_string_style`;
|
|
67
|
-
return {
|
|
68
|
-
[control]: {
|
|
69
|
-
color: this.model!._color[0],
|
|
70
|
-
bg_color: this.model!._color[0],
|
|
71
|
-
},
|
|
72
|
-
} as DatetimeStyleOpts;
|
|
73
|
-
} else {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
45
|
+
export function isMetaColumn(name: string): boolean {
|
|
46
|
+
return META_COLUMN_RE.test(name);
|
|
76
47
|
}
|
package/src/ts/model/toolbar.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
import { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
|
|
13
14
|
import type {
|
|
14
15
|
DatagridModel,
|
|
15
16
|
DatagridPluginElement,
|
|
@@ -26,7 +27,10 @@ export const EDIT_MODES: readonly EditMode[] = [
|
|
|
26
27
|
] as const;
|
|
27
28
|
|
|
28
29
|
function isSelectRowTreeAvailable(model?: DatagridModel): boolean {
|
|
29
|
-
if (!model)
|
|
30
|
+
if (!model) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
return (
|
|
31
35
|
model._config.group_by.length > 0 &&
|
|
32
36
|
model._config.group_rollup_mode !== "flat"
|
|
@@ -45,10 +49,11 @@ export function toggle_edit_mode(
|
|
|
45
49
|
EDIT_MODES[idx] === "SELECT_ROW_TREE" &&
|
|
46
50
|
!isSelectRowTreeAvailable(this.model)
|
|
47
51
|
);
|
|
52
|
+
|
|
48
53
|
mode = EDIT_MODES[idx];
|
|
49
54
|
}
|
|
50
55
|
|
|
51
|
-
(this.parentElement as
|
|
56
|
+
(this.parentElement as HTMLPerspectiveViewerElement)?.setSelection?.();
|
|
52
57
|
this._edit_mode = mode;
|
|
53
58
|
if (this.model) {
|
|
54
59
|
this.model._edit_mode = mode;
|
|
@@ -59,6 +64,10 @@ export function toggle_edit_mode(
|
|
|
59
64
|
};
|
|
60
65
|
}
|
|
61
66
|
|
|
67
|
+
(this.parentElement as HTMLPerspectiveViewerElement)?.restore?.({
|
|
68
|
+
plugin_config: { edit_mode: mode },
|
|
69
|
+
});
|
|
70
|
+
|
|
62
71
|
if (this._edit_button !== undefined) {
|
|
63
72
|
this._edit_button.dataset.editMode = mode;
|
|
64
73
|
}
|
|
@@ -91,7 +91,9 @@ export async function activate(
|
|
|
91
91
|
area: SelectionArea,
|
|
92
92
|
isDeselect: boolean,
|
|
93
93
|
) => {
|
|
94
|
-
if (model._edit_mode !== "SELECT_ROW_TREE")
|
|
94
|
+
if (model._edit_mode !== "SELECT_ROW_TREE") {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
95
97
|
|
|
96
98
|
// Store the selected row identity on the model so it persists
|
|
97
99
|
// even when the selected row scrolls out of the viewport.
|
|
@@ -0,0 +1,187 @@
|
|
|
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 type { ColumnType } from "@perspective-dev/client";
|
|
14
|
+
import type { DatagridPluginElement } from "../types.js";
|
|
15
|
+
|
|
16
|
+
interface ViewerConfigLike {
|
|
17
|
+
group_by?: string[];
|
|
18
|
+
group_rollup_mode?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type ControlSpec = Record<string, unknown> & { kind: string };
|
|
22
|
+
|
|
23
|
+
export interface ColumnConfigSchema {
|
|
24
|
+
fields: ControlSpec[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Plugin schema for the Datagrid column-settings sidebar. Returns the
|
|
29
|
+
* controls the viewer should render in the Style tab for a given column.
|
|
30
|
+
*
|
|
31
|
+
* Each entry in `fields` is a `ControlSpec` discriminated by `kind`.
|
|
32
|
+
* Composite kinds (`NumberStyle`, `DatetimeFormat`, `StringFormat`,
|
|
33
|
+
* `NumberFormat`, `AggregateDepth`) own a fixed key namespace and
|
|
34
|
+
* carry only their `default`. Primitive kinds (`Enum`, `Bool`, `Color`,
|
|
35
|
+
* etc.) carry their own `key` (storage) and `label` (UI) inline.
|
|
36
|
+
*
|
|
37
|
+
* Aggregate Depth is plugin-owned — surfaced only inside the Datagrid
|
|
38
|
+
* because rollup-mode pivots are a Datagrid concern. Emitted only when
|
|
39
|
+
* the active view has a non-empty `group_by` and rollup mode is `Rollup`.
|
|
40
|
+
*/
|
|
41
|
+
interface ColumnStats {
|
|
42
|
+
abs_max?: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function column_config_schema(
|
|
46
|
+
this: DatagridPluginElement,
|
|
47
|
+
type: ColumnType,
|
|
48
|
+
_group: string | undefined,
|
|
49
|
+
_column_name: string,
|
|
50
|
+
current_value: Record<string, unknown> | null,
|
|
51
|
+
viewer_config?: ViewerConfigLike,
|
|
52
|
+
column_stats?: ColumnStats,
|
|
53
|
+
): ColumnConfigSchema {
|
|
54
|
+
const fields: ControlSpec[] = [];
|
|
55
|
+
|
|
56
|
+
if (type === "integer" || type === "float") {
|
|
57
|
+
const pos_fg = this.model!._pos_fg_color[0];
|
|
58
|
+
const neg_fg = this.model!._neg_fg_color[0];
|
|
59
|
+
const pos_bg = this.model!._pos_bg_color[0];
|
|
60
|
+
const neg_bg = this.model!._neg_bg_color[0];
|
|
61
|
+
|
|
62
|
+
fields.push({
|
|
63
|
+
kind: "Enum",
|
|
64
|
+
key: "number_fg_mode",
|
|
65
|
+
default: "color",
|
|
66
|
+
variants: [
|
|
67
|
+
{ value: "disabled", label: "Disabled" },
|
|
68
|
+
{ value: "color", label: "Color" },
|
|
69
|
+
{ value: "bar", label: "Bar" },
|
|
70
|
+
{ value: "label-bar", label: "Gradient" },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const fg_mode = (current_value?.number_fg_mode as string) ?? "color";
|
|
75
|
+
if (fg_mode !== "disabled") {
|
|
76
|
+
fields.push({
|
|
77
|
+
kind: "ColorRange",
|
|
78
|
+
key_pos: "pos_fg_color",
|
|
79
|
+
key_neg: "neg_fg_color",
|
|
80
|
+
default_pos: pos_fg,
|
|
81
|
+
default_neg: neg_fg,
|
|
82
|
+
is_gradient: false,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (fg_mode === "bar" || fg_mode === "label-bar") {
|
|
87
|
+
fields.push({
|
|
88
|
+
kind: "Number",
|
|
89
|
+
key: "fg_gradient",
|
|
90
|
+
default: column_stats?.abs_max ?? 0,
|
|
91
|
+
include: true,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fields.push({
|
|
96
|
+
kind: "Enum",
|
|
97
|
+
key: "number_bg_mode",
|
|
98
|
+
default: "disabled",
|
|
99
|
+
variants: [
|
|
100
|
+
{ value: "disabled", label: "Disabled" },
|
|
101
|
+
{ value: "color", label: "Color" },
|
|
102
|
+
{ value: "gradient", label: "Gradient" },
|
|
103
|
+
{ value: "pulse", label: "Pulse" },
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const bg_mode = (current_value?.number_bg_mode as string) ?? "disabled";
|
|
108
|
+
if (bg_mode !== "disabled") {
|
|
109
|
+
fields.push({
|
|
110
|
+
kind: "ColorRange",
|
|
111
|
+
key_pos: "pos_bg_color",
|
|
112
|
+
key_neg: "neg_bg_color",
|
|
113
|
+
default_pos: pos_bg,
|
|
114
|
+
default_neg: neg_bg,
|
|
115
|
+
is_gradient: bg_mode === "gradient" || bg_mode === "pulse",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (bg_mode === "gradient") {
|
|
120
|
+
fields.push({
|
|
121
|
+
kind: "Number",
|
|
122
|
+
key: "bg_gradient",
|
|
123
|
+
include: true,
|
|
124
|
+
default: column_stats?.abs_max ?? 0,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
fields.push({ kind: "NumberFormat" });
|
|
129
|
+
} else if (type === "date" || type === "datetime") {
|
|
130
|
+
fields.push({ kind: "DatetimeFormat" });
|
|
131
|
+
|
|
132
|
+
fields.push({
|
|
133
|
+
kind: "Enum",
|
|
134
|
+
key: "datetime_color_mode",
|
|
135
|
+
default: "none",
|
|
136
|
+
variants: [
|
|
137
|
+
{ value: "none", label: "None" },
|
|
138
|
+
{ value: "foreground", label: "Foreground" },
|
|
139
|
+
{ value: "background", label: "Background" },
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const dt_mode =
|
|
144
|
+
(current_value?.datetime_color_mode as string) ?? "none";
|
|
145
|
+
|
|
146
|
+
if (dt_mode !== "none") {
|
|
147
|
+
fields.push({
|
|
148
|
+
kind: "Color",
|
|
149
|
+
key: "color",
|
|
150
|
+
default: this.model!._color[0],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
} else if (type === "string") {
|
|
154
|
+
fields.push({ kind: "StringFormat" });
|
|
155
|
+
|
|
156
|
+
fields.push({
|
|
157
|
+
kind: "Enum",
|
|
158
|
+
key: "string_color_mode",
|
|
159
|
+
default: "none",
|
|
160
|
+
variants: [
|
|
161
|
+
{ value: "none", label: "None" },
|
|
162
|
+
{ value: "foreground", label: "Foreground" },
|
|
163
|
+
{ value: "background", label: "Background" },
|
|
164
|
+
{ value: "series", label: "Series" },
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const str_mode = (current_value?.string_color_mode as string) ?? "none";
|
|
169
|
+
if (str_mode !== "none") {
|
|
170
|
+
fields.push({
|
|
171
|
+
kind: "Color",
|
|
172
|
+
key: "color",
|
|
173
|
+
default: this.model!._color[0],
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const group_by = viewer_config?.group_by ?? [];
|
|
179
|
+
const is_rollup =
|
|
180
|
+
(viewer_config?.group_rollup_mode ?? "rollup") === "rollup";
|
|
181
|
+
|
|
182
|
+
if (group_by.length > 0 && is_rollup) {
|
|
183
|
+
fields.push({ kind: "AggregateDepth" });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { fields };
|
|
187
|
+
}
|
package/src/ts/plugin/draw.ts
CHANGED
package/src/ts/plugin/restore.ts
CHANGED
|
@@ -92,16 +92,20 @@ export function restore(
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
if ("edit_mode" in token
|
|
96
|
-
if (EDIT_MODES.indexOf(token.edit_mode) !== -1) {
|
|
95
|
+
if ("edit_mode" in token) {
|
|
96
|
+
if (EDIT_MODES.indexOf(token.edit_mode!) !== -1) {
|
|
97
97
|
toggle_edit_mode.call(this, token.edit_mode);
|
|
98
98
|
} else {
|
|
99
99
|
console.error("Unknown edit mode " + token.edit_mode);
|
|
100
100
|
}
|
|
101
|
+
} else {
|
|
102
|
+
toggle_edit_mode.call(this, "READ_ONLY");
|
|
101
103
|
}
|
|
102
104
|
|
|
103
105
|
if ("scroll_lock" in token) {
|
|
104
106
|
toggle_scroll_lock.call(this, token.scroll_lock);
|
|
107
|
+
} else {
|
|
108
|
+
toggle_scroll_lock.call(this, false);
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
const datagrid = this.regular_table;
|
package/src/ts/plugin/save.ts
CHANGED
|
@@ -11,11 +11,7 @@
|
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
13
|
import { save_column_size_overrides } from "../model/column_overrides.js";
|
|
14
|
-
import type {
|
|
15
|
-
DatagridPluginElement,
|
|
16
|
-
DatagridPluginConfig,
|
|
17
|
-
EditMode,
|
|
18
|
-
} from "../types.js";
|
|
14
|
+
import type { DatagridPluginElement, DatagridPluginConfig } from "../types.js";
|
|
19
15
|
|
|
20
16
|
export function save(
|
|
21
17
|
this: DatagridPluginElement,
|
|
@@ -185,8 +185,11 @@ export function applyBodyCellStyles(
|
|
|
185
185
|
td.classList.toggle("psp-select-region-inactive", isSub);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
// } else if (
|
|
189
|
+
// model._edit_mode === "READ_ONLY" ||
|
|
190
|
+
// model._edit_mode === "EDIT"
|
|
191
|
+
// ) {
|
|
192
|
+
// td.classList.toggle("psp-select-region", false);
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
// Apply editable styling (if editable)
|
|
@@ -212,6 +215,7 @@ export function applyBodyCellStyles(
|
|
|
212
215
|
if (isEditable !== td.hasAttribute("contenteditable")) {
|
|
213
216
|
td.toggleAttribute("contenteditable", isEditable);
|
|
214
217
|
}
|
|
218
|
+
|
|
215
219
|
td.classList.toggle("boolean-editable", false);
|
|
216
220
|
}
|
|
217
221
|
} else {
|
|
@@ -72,7 +72,10 @@ export function style_selected_column(
|
|
|
72
72
|
if (model._config.columns.length > 1) {
|
|
73
73
|
for (const r of regularTable.querySelectorAll("td")) {
|
|
74
74
|
const meta = regularTable.getMeta(r);
|
|
75
|
-
if (!meta?.column_header)
|
|
75
|
+
if (!meta?.column_header) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
76
79
|
const isOpen =
|
|
77
80
|
meta.column_header[
|
|
78
81
|
meta.column_header.length - 2
|
|
@@ -104,8 +107,9 @@ export function styleColumnHeaderRow(
|
|
|
104
107
|
!metadata ||
|
|
105
108
|
metadata.type === "body" ||
|
|
106
109
|
metadata.type === "row_header"
|
|
107
|
-
)
|
|
110
|
+
) {
|
|
108
111
|
continue;
|
|
112
|
+
}
|
|
109
113
|
|
|
110
114
|
const column_name =
|
|
111
115
|
metadata.column_header?.[model._config.split_by.length];
|
|
@@ -27,7 +27,9 @@ export function applyColumnHeaderStyles(
|
|
|
27
27
|
regularTable: RegularTableElement,
|
|
28
28
|
viewer: HTMLPerspectiveViewerElement,
|
|
29
29
|
): void {
|
|
30
|
-
if (headerRows.length === 0)
|
|
30
|
+
if (headerRows.length === 0) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
31
33
|
|
|
32
34
|
// Style selected column for settings panel
|
|
33
35
|
const selectedColumn = model._column_settings_selected_column;
|
|
@@ -68,7 +70,9 @@ export function applyColumnHeaderStyles(
|
|
|
68
70
|
for (let i = 0; i < titlesRow.cells.length; i++) {
|
|
69
71
|
const title = titlesRow.cells[i]?.element;
|
|
70
72
|
const editBtn = editBtnsRow.cells[i]?.element;
|
|
71
|
-
if (!title || !editBtn)
|
|
73
|
+
if (!title || !editBtn) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
72
76
|
|
|
73
77
|
const open = title.textContent === selectedColumn;
|
|
74
78
|
title.classList.toggle("psp-menu-open", open);
|
|
@@ -37,6 +37,7 @@ export function applyFocusStyle(
|
|
|
37
37
|
if (host.activeElement !== td) {
|
|
38
38
|
td.focus({ preventScroll: true });
|
|
39
39
|
}
|
|
40
|
+
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
}
|
|
@@ -82,6 +83,7 @@ export function focusSelectedCell(
|
|
|
82
83
|
if (host.activeElement !== cell) {
|
|
83
84
|
(cell as HTMLElement).focus({ preventScroll: true });
|
|
84
85
|
}
|
|
86
|
+
|
|
85
87
|
return true;
|
|
86
88
|
}
|
|
87
89
|
}
|
|
@@ -28,12 +28,14 @@ export function applyGroupHeaderStyles(
|
|
|
28
28
|
let marked = new Set<number>();
|
|
29
29
|
|
|
30
30
|
for (let y = 0; y < headerRows.length; y++) {
|
|
31
|
-
const {
|
|
31
|
+
const { cells } = headerRows[y];
|
|
32
32
|
const tops = new Set<number>();
|
|
33
33
|
|
|
34
34
|
for (let x = 0; x < cells.length; x++) {
|
|
35
35
|
const { element: td, metadata } = cells[x];
|
|
36
|
-
if (!metadata)
|
|
36
|
+
if (!metadata) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
37
39
|
|
|
38
40
|
td.style.backgroundColor = "";
|
|
39
41
|
|
|
@@ -63,13 +65,17 @@ export function applyGroupHeaderStyles(
|
|
|
63
65
|
|
|
64
66
|
// Calculate spanning for psp-is-top
|
|
65
67
|
let xx = x;
|
|
66
|
-
for (; m[y] && m[y][xx]; ++xx)
|
|
68
|
+
for (; m[y] && m[y][xx]; ++xx) {}
|
|
69
|
+
|
|
67
70
|
tops.add(xx);
|
|
68
71
|
|
|
69
72
|
const cell = td;
|
|
70
73
|
for (let tx = xx; tx < xx + cell.colSpan; ++tx) {
|
|
71
74
|
for (let ty = y; ty < y + cell.rowSpan; ++ty) {
|
|
72
|
-
if (!m[ty])
|
|
75
|
+
if (!m[ty]) {
|
|
76
|
+
m[ty] = [];
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
m[ty][tx] = true;
|
|
74
80
|
}
|
|
75
81
|
}
|