@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.
Files changed (37) hide show
  1. package/dist/cdn/perspective-viewer-datagrid.js +4 -4
  2. package/dist/cdn/perspective-viewer-datagrid.js.map +4 -4
  3. package/dist/esm/custom_elements/datagrid.d.ts +12 -17
  4. package/dist/esm/model/meta_columns.d.ts +1 -0
  5. package/dist/esm/perspective-viewer-datagrid.js +3 -3
  6. package/dist/esm/perspective-viewer-datagrid.js.map +4 -4
  7. package/dist/esm/plugin/column_config_schema.d.ts +31 -0
  8. package/package.json +1 -1
  9. package/src/ts/color_utils.ts +50 -13
  10. package/src/ts/custom_elements/datagrid.ts +63 -48
  11. package/src/ts/data_listener/format_tree_header.ts +2 -2
  12. package/src/ts/data_listener/formatter_cache.ts +8 -95
  13. package/src/ts/data_listener/index.ts +9 -3
  14. package/src/ts/event_handlers/click/edit_click.ts +3 -0
  15. package/src/ts/event_handlers/dispatch_click.ts +4 -1
  16. package/src/ts/event_handlers/expand_collapse.ts +4 -1
  17. package/src/ts/event_handlers/header_click.ts +9 -3
  18. package/src/ts/event_handlers/keydown/edit_keydown.ts +11 -2
  19. package/src/ts/event_handlers/select_region.ts +15 -4
  20. package/src/ts/event_handlers/sort.ts +4 -1
  21. package/src/ts/get_cell_config.ts +10 -3
  22. package/src/ts/model/column_overrides.ts +3 -5
  23. package/src/ts/model/create.ts +22 -5
  24. package/src/ts/{plugin/column_style_controls.ts → model/meta_columns.ts} +33 -62
  25. package/src/ts/model/toolbar.ts +11 -2
  26. package/src/ts/plugin/activate.ts +3 -1
  27. package/src/ts/plugin/column_config_schema.ts +187 -0
  28. package/src/ts/plugin/draw.ts +1 -0
  29. package/src/ts/plugin/restore.ts +6 -2
  30. package/src/ts/plugin/save.ts +1 -5
  31. package/src/ts/style_handlers/body.ts +6 -2
  32. package/src/ts/style_handlers/column_header.ts +6 -2
  33. package/src/ts/style_handlers/consolidated.ts +1 -0
  34. package/src/ts/style_handlers/editable.ts +6 -2
  35. package/src/ts/style_handlers/focus.ts +2 -0
  36. package/src/ts/style_handlers/group_header.ts +10 -4
  37. package/dist/esm/plugin/column_style_controls.d.ts +0 -28
@@ -0,0 +1,31 @@
1
+ import type { ColumnType } from "@perspective-dev/client";
2
+ import type { DatagridPluginElement } from "../types.js";
3
+ interface ViewerConfigLike {
4
+ group_by?: string[];
5
+ group_rollup_mode?: string;
6
+ }
7
+ type ControlSpec = Record<string, unknown> & {
8
+ kind: string;
9
+ };
10
+ export interface ColumnConfigSchema {
11
+ fields: ControlSpec[];
12
+ }
13
+ /**
14
+ * Plugin schema for the Datagrid column-settings sidebar. Returns the
15
+ * controls the viewer should render in the Style tab for a given column.
16
+ *
17
+ * Each entry in `fields` is a `ControlSpec` discriminated by `kind`.
18
+ * Composite kinds (`NumberStyle`, `DatetimeFormat`, `StringFormat`,
19
+ * `NumberFormat`, `AggregateDepth`) own a fixed key namespace and
20
+ * carry only their `default`. Primitive kinds (`Enum`, `Bool`, `Color`,
21
+ * etc.) carry their own `key` (storage) and `label` (UI) inline.
22
+ *
23
+ * Aggregate Depth is plugin-owned — surfaced only inside the Datagrid
24
+ * because rollup-mode pivots are a Datagrid concern. Emitted only when
25
+ * the active view has a non-empty `group_by` and rollup mode is `Rollup`.
26
+ */
27
+ interface ColumnStats {
28
+ abs_max?: number;
29
+ }
30
+ export default function column_config_schema(this: DatagridPluginElement, type: ColumnType, _group: string | undefined, _column_name: string, current_value: Record<string, unknown> | null, viewer_config?: ViewerConfigLike, column_stats?: ColumnStats): ColumnConfigSchema;
31
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perspective-dev/viewer-datagrid",
3
- "version": "4.4.1",
3
+ "version": "4.5.0",
4
4
  "description": "Perspective datagrid plugin based on `regular-table`",
5
5
  "unpkg": "dist/cdn/perspective-viewer-datagrid.js",
6
6
  "jsdelivr": "dist/cdn/perspective-viewer-datagrid.js",
@@ -28,13 +28,18 @@ function parse_hex(input: string): RGB | null {
28
28
  const r = parseInt(s[0] + s[0], 16);
29
29
  const g = parseInt(s[1] + s[1], 16);
30
30
  const b = parseInt(s[2] + s[2], 16);
31
- if (!isNaN(r) && !isNaN(g) && !isNaN(b)) return [r, g, b];
31
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
32
+ return [r, g, b];
33
+ }
32
34
  } else if (s.length === 6 || s.length === 8) {
33
35
  const r = parseInt(s.slice(0, 2), 16);
34
36
  const g = parseInt(s.slice(2, 4), 16);
35
37
  const b = parseInt(s.slice(4, 6), 16);
36
- if (!isNaN(r) && !isNaN(g) && !isNaN(b)) return [r, g, b];
38
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
39
+ return [r, g, b];
40
+ }
37
41
  }
42
+
38
43
  return null;
39
44
  }
40
45
 
@@ -43,7 +48,10 @@ function parse_rgb_fn(input: string): RGB | null {
43
48
  const m = input.match(
44
49
  /^rgba?\(\s*([\d.]+)\s*[, ]\s*([\d.]+)\s*[, ]\s*([\d.]+)/i,
45
50
  );
46
- if (!m) return null;
51
+ if (!m) {
52
+ return null;
53
+ }
54
+
47
55
  return [
48
56
  Math.round(parseFloat(m[1])),
49
57
  Math.round(parseFloat(m[2])),
@@ -58,7 +66,11 @@ function parse_via_canvas(input: string): RGB {
58
66
  canvas.width = canvas.height = 1;
59
67
  parse_ctx = canvas.getContext("2d");
60
68
  }
61
- if (!parse_ctx) return [0, 0, 0];
69
+
70
+ if (!parse_ctx) {
71
+ return [0, 0, 0];
72
+ }
73
+
62
74
  parse_ctx.fillStyle = "#000";
63
75
  parse_ctx.fillStyle = input;
64
76
  const normalized = parse_ctx.fillStyle as string;
@@ -69,7 +81,10 @@ function parse_via_canvas(input: string): RGB {
69
81
  export function parseColor(input: string): RGB {
70
82
  const key = input.trim();
71
83
  const cached = parse_cache.get(key);
72
- if (cached) return cached;
84
+ if (cached) {
85
+ return cached;
86
+ }
87
+
73
88
  const rgb = parse_hex(key) ?? parse_rgb_fn(key) ?? parse_via_canvas(key);
74
89
  parse_cache.set(key, rgb);
75
90
  return rgb;
@@ -99,10 +114,15 @@ export function rgbToHsl([r, g, b]: RGB): HSL {
99
114
  let s = 0;
100
115
  if (d !== 0) {
101
116
  s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
102
- if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) * 60;
103
- else if (max === gn) h = ((bn - rn) / d + 2) * 60;
104
- else h = ((rn - gn) / d + 4) * 60;
117
+ if (max === rn) {
118
+ h = ((gn - bn) / d + (gn < bn ? 6 : 0)) * 60;
119
+ } else if (max === gn) {
120
+ h = ((bn - rn) / d + 2) * 60;
121
+ } else {
122
+ h = ((rn - gn) / d + 4) * 60;
123
+ }
105
124
  }
125
+
106
126
  return [h, s, l];
107
127
  }
108
128
 
@@ -113,16 +133,33 @@ export function hslToRgb([h, s, l]: HSL): RGB {
113
133
  const v = Math.round(l * 255);
114
134
  return [v, v, v];
115
135
  }
136
+
116
137
  const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
117
138
  const p = 2 * l - q;
118
139
  const f = (t: number): number => {
119
- if (t < 0) t += 1;
120
- if (t > 1) t -= 1;
121
- if (t < 1 / 6) return p + (q - p) * 6 * t;
122
- if (t < 1 / 2) return q;
123
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
140
+ if (t < 0) {
141
+ t += 1;
142
+ }
143
+
144
+ if (t > 1) {
145
+ t -= 1;
146
+ }
147
+
148
+ if (t < 1 / 6) {
149
+ return p + (q - p) * 6 * t;
150
+ }
151
+
152
+ if (t < 1 / 2) {
153
+ return q;
154
+ }
155
+
156
+ if (t < 2 / 3) {
157
+ return p + (q - p) * (2 / 3 - t) * 6;
158
+ }
159
+
124
160
  return p;
125
161
  };
162
+
126
163
  return [
127
164
  Math.round(f(hn + 1 / 3) * 255),
128
165
  Math.round(f(hn) * 255),
@@ -15,16 +15,17 @@ import { activate } from "../plugin/activate.js";
15
15
  import { restore } from "../plugin/restore.js";
16
16
  import { save } from "../plugin/save.js";
17
17
  import { draw } from "../plugin/draw.js";
18
- import column_style_controls, {
19
- ColumnStyleOpts,
20
- } from "../plugin/column_style_controls.js";
18
+ import column_config_schema, {
19
+ ColumnConfigSchema,
20
+ } from "../plugin/column_config_schema.js";
21
21
  import datagridStyles from "../../../dist/css/perspective-viewer-datagrid.css";
22
22
  import { format_raw } from "../data_listener/format_cell.js";
23
+ import { sourceColumn } from "@perspective-dev/viewer/src/ts/column-format.js";
23
24
 
24
25
  import type { View, ViewWindow } from "@perspective-dev/client";
25
26
  import type {
26
- HTMLPerspectiveViewerElement,
27
27
  IPerspectiveViewerPlugin,
28
+ PluginStaticConfig,
28
29
  } from "@perspective-dev/viewer";
29
30
  import type {
30
31
  DatagridModel,
@@ -115,44 +116,64 @@ export class HTMLPerspectiveViewerDatagridPluginElement
115
116
  return await activate.call(this, view);
116
117
  }
117
118
 
118
- get name(): string {
119
- return "Datagrid";
119
+ get_static_config(): PluginStaticConfig {
120
+ return {
121
+ name: "Datagrid",
122
+ category: "Basic",
123
+ select_mode: "toggle",
124
+ config_column_names: ["Columns"],
125
+ group_rollup_modes: ["rollup", "flat", "total"],
126
+ // Higher priority than the chart plugins so the Datagrid is
127
+ // loaded by default.
128
+ priority: 1,
129
+ can_render_column_styles: true,
130
+ };
120
131
  }
121
132
 
122
- get category(): string {
123
- return "Basic";
133
+ plugin_config_schema(): ColumnConfigSchema {
134
+ const fields = [];
135
+ fields.push({
136
+ kind: "Enum",
137
+ key: "edit_mode",
138
+ default: "READ_ONLY",
139
+ variants: [
140
+ { value: "EDIT", label: "Edit" },
141
+ { value: "READ_ONLY", label: "Read-only" },
142
+ { value: "SELECT_ROW", label: "Row Select" },
143
+ { value: "SELECT_COLUMN", label: "Column Select" },
144
+ { value: "SELECT_REGION", label: "Region Select" },
145
+ { value: "SELECT_ROW_TREE", label: "Tree Select" },
146
+ ],
147
+ });
148
+
149
+ fields.push({
150
+ kind: "Bool",
151
+ key: "scroll_lock",
152
+ default: false,
153
+ });
154
+
155
+ return {
156
+ fields,
157
+ };
124
158
  }
125
159
 
126
- get select_mode(): string {
127
- return "toggle";
128
- }
129
-
130
- get min_config_columns(): number | undefined {
131
- return undefined;
132
- }
133
-
134
- get config_column_names(): string[] {
135
- return ["Columns"];
136
- }
137
-
138
- get group_rollups(): string[] {
139
- return ["rollup", "flat", "total"];
140
- }
141
-
142
- /**
143
- * Give the Datagrid a higher priority so it is loaded
144
- * over the default charts by default.
145
- */
146
- get priority(): number {
147
- return 1;
148
- }
149
-
150
- can_render_column_styles(type: string, _group: string): boolean {
151
- return type !== "boolean";
152
- }
153
-
154
- column_style_controls(type: string, group: string): ColumnStyleOpts {
155
- return column_style_controls.call(this, type as any, group);
160
+ column_config_schema(
161
+ type: string,
162
+ group: string | undefined,
163
+ column_name: string,
164
+ current_value: Record<string, unknown> | null,
165
+ viewer_config?: { group_by?: string[]; group_rollup_mode?: string },
166
+ column_stats?: { abs_max: number },
167
+ ): ColumnConfigSchema {
168
+ return column_config_schema.call(
169
+ this,
170
+ type as any,
171
+ group,
172
+ column_name,
173
+ current_value,
174
+ viewer_config,
175
+ column_stats,
176
+ );
156
177
  }
157
178
 
158
179
  async draw(view: View): Promise<void> {
@@ -172,9 +193,7 @@ export class HTMLPerspectiveViewerDatagridPluginElement
172
193
  }
173
194
  }
174
195
 
175
- async render(viewport?: ViewWindow): Promise<string> {
176
- const viewer = this.parentElement as HTMLPerspectiveViewerElement;
177
- const view = await viewer.getView();
196
+ async render(view: View, viewport?: ViewWindow): Promise<string> {
178
197
  const json = await view.to_columns(viewport as any);
179
198
  const cols = await view.column_paths(viewport as any);
180
199
 
@@ -194,7 +213,7 @@ export class HTMLPerspectiveViewerDatagridPluginElement
194
213
  const pluginConfig = (this.regular_table as any)[
195
214
  PRIVATE_PLUGIN_SYMBOL
196
215
  ] as ColumnsConfig | undefined;
197
- const columnName = col_name.split("|").at(-1)!;
216
+ const columnName = sourceColumn(col_name);
198
217
  const formatter = format_raw(
199
218
  type,
200
219
  pluginConfig?.[columnName] || {},
@@ -206,6 +225,7 @@ export class HTMLPerspectiveViewerDatagridPluginElement
206
225
  out += col[ridx] + "\t";
207
226
  }
208
227
  }
228
+
209
229
  out += "\n";
210
230
  }
211
231
 
@@ -235,12 +255,7 @@ export class HTMLPerspectiveViewerDatagridPluginElement
235
255
  return restore.call(this, token, columns_config ?? {});
236
256
  }
237
257
 
238
- async restyle(view: View): Promise<void> {
239
- // Get view from model if available, otherwise no-op
240
- if (this.model?._view) {
241
- await this.draw(view);
242
- }
243
- }
258
+ restyle() {}
244
259
 
245
260
  delete(): void {
246
261
  this.disconnectedCallback();
@@ -27,7 +27,7 @@ export function* format_tree_header_row_path(
27
27
  ): Generator<RowHeaderCell[]> {
28
28
  const plugins: ColumnsConfig =
29
29
  (regularTable as any)[PRIVATE_PLUGIN_SYMBOL] || {};
30
- for (let path of paths) {
30
+ for (const path of paths) {
31
31
  const fullPath: unknown[] = ["TOTAL", ...path];
32
32
  const last = fullPath[fullPath.length - 1];
33
33
  let newPath: RowHeaderCell[] = fullPath
@@ -61,7 +61,7 @@ export function* format_flat_header_row_path(
61
61
  const plugins: ColumnsConfig =
62
62
  (regularTable as any)[PRIVATE_PLUGIN_SYMBOL] || {};
63
63
 
64
- for (let path of paths) {
64
+ for (const path of paths) {
65
65
  yield path.map((part, i) =>
66
66
  format_cell.call(this, row_headers[i], part, plugins, true),
67
67
  ) as RowHeaderCell[];
@@ -11,6 +11,11 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  import type { ColumnType } from "@perspective-dev/client";
14
+ import {
15
+ createDateFormatter,
16
+ createDatetimeFormatter,
17
+ createNumberFormatter,
18
+ } from "@perspective-dev/viewer/src/ts/column-format.js";
14
19
  import type { ColumnConfig } from "../types.js";
15
20
 
16
21
  export interface Formatter {
@@ -26,30 +31,6 @@ class BooleanFormatter implements Formatter {
26
31
  // PluginConfig is a subset of ColumnConfig with the formatting properties
27
32
  type PluginConfig = Pick<ColumnConfig, "date_format" | "number_format">;
28
33
 
29
- const LEGACY_CONFIG: Record<
30
- string,
31
- { format: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions }
32
- > = {
33
- float: {
34
- format: {
35
- style: "decimal",
36
- minimumFractionDigits: 2,
37
- maximumFractionDigits: 2,
38
- },
39
- },
40
- datetime: {
41
- format: {
42
- dateStyle: "short",
43
- timeStyle: "medium",
44
- } as Intl.DateTimeFormatOptions,
45
- },
46
- date: {
47
- format: {
48
- dateStyle: "short",
49
- } as Intl.DateTimeFormatOptions,
50
- },
51
- };
52
-
53
34
  export class FormatterCache {
54
35
  private _formatters: Map<string, Formatter | false>;
55
36
 
@@ -61,89 +42,21 @@ export class FormatterCache {
61
42
  _type: ColumnType,
62
43
  plugin: PluginConfig,
63
44
  ): Intl.DateTimeFormat {
64
- if (plugin.date_format?.format !== "custom") {
65
- const options: Intl.DateTimeFormatOptions = {
66
- timeZone: plugin.date_format?.timeZone,
67
- dateStyle:
68
- plugin.date_format?.dateStyle === "disabled"
69
- ? undefined
70
- : (plugin.date_format?.dateStyle ?? "short"),
71
- timeStyle:
72
- plugin.date_format?.timeStyle === "disabled"
73
- ? undefined
74
- : (plugin.date_format?.timeStyle ?? "medium"),
75
- };
76
-
77
- return new Intl.DateTimeFormat(
78
- navigator.languages as string[],
79
- options,
80
- );
81
- } else {
82
- const options: Intl.DateTimeFormatOptions = {
83
- timeZone: plugin.date_format?.timeZone,
84
- hour12: plugin.date_format?.hour12 ?? true,
85
- fractionalSecondDigits:
86
- plugin.date_format?.fractionalSecondDigits,
87
- };
88
-
89
- if (plugin.date_format?.year !== "disabled") {
90
- options.year = plugin.date_format?.year ?? "2-digit";
91
- }
92
- if (plugin.date_format?.month !== "disabled") {
93
- options.month = plugin.date_format?.month ?? "numeric";
94
- }
95
- if (plugin.date_format?.day !== "disabled") {
96
- options.day = plugin.date_format?.day ?? "numeric";
97
- }
98
- if (
99
- plugin.date_format?.weekday &&
100
- plugin.date_format?.weekday !== "disabled"
101
- ) {
102
- options.weekday = plugin.date_format.weekday;
103
- }
104
- if (plugin.date_format?.hour !== "disabled") {
105
- options.hour = plugin.date_format?.hour ?? "numeric";
106
- }
107
- if (plugin.date_format?.minute !== "disabled") {
108
- options.minute = plugin.date_format?.minute ?? "numeric";
109
- }
110
- if (plugin.date_format?.second !== "disabled") {
111
- options.second = plugin.date_format?.second ?? "numeric";
112
- }
113
-
114
- return new Intl.DateTimeFormat(
115
- navigator.languages as string[],
116
- options,
117
- );
118
- }
45
+ return createDatetimeFormatter(plugin.date_format);
119
46
  }
120
47
 
121
48
  private create_date_formatter(
122
49
  _type: ColumnType,
123
50
  plugin: PluginConfig,
124
51
  ): Intl.DateTimeFormat {
125
- const options: Intl.DateTimeFormatOptions = {
126
- timeZone: "utc",
127
- dateStyle:
128
- plugin.date_format?.dateStyle === "disabled"
129
- ? undefined
130
- : (plugin.date_format?.dateStyle ?? "short"),
131
- };
132
-
133
- return new Intl.DateTimeFormat(
134
- navigator.languages as string[],
135
- options,
136
- );
52
+ return createDateFormatter(plugin.date_format);
137
53
  }
138
54
 
139
55
  private create_number_formatter(
140
56
  type: ColumnType,
141
57
  plugin: PluginConfig,
142
58
  ): Intl.NumberFormat {
143
- const format =
144
- plugin.number_format ??
145
- (LEGACY_CONFIG[type]?.format as Intl.NumberFormatOptions);
146
- return new Intl.NumberFormat(navigator.languages as string[], format);
59
+ return createNumberFormatter(type, plugin.number_format);
147
60
  }
148
61
 
149
62
  private create_boolean_formatter(
@@ -11,6 +11,7 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  import { PRIVATE_PLUGIN_SYMBOL } from "../types.js";
14
+ import { isMetaColumn } from "../model/meta_columns.js";
14
15
  import { format_cell } from "./format_cell.js";
15
16
  import {
16
17
  format_flat_header_row_path,
@@ -84,9 +85,14 @@ export function createDataListener(
84
85
  num_columns = z;
85
86
  columns = JSON.parse(x as string) as ColumnData;
86
87
  const y = Object.keys(columns);
87
- const new_col_paths = y.filter(
88
- (x) => x !== "__ROW_PATH__" && x !== "__ID__",
89
- );
88
+ // `isMetaColumn` covers `__ROW_PATH__`, `__ID__`,
89
+ // `__GROUPING_ID__`, and the per-level `__ROW_PATH_<n>__`
90
+ // columns the DuckDB virtual server emits inline alongside
91
+ // the JSON sidecar. Exact-match against `__ROW_PATH__` /
92
+ // `__ID__` (the previous filter) misses the per-level
93
+ // form and the virtual server's columns leak into the
94
+ // visible grid.
95
+ const new_col_paths = y.filter((x) => !isMetaColumn(x));
90
96
 
91
97
  let changed_cols = false;
92
98
  for (let i = 0; i < new_col_paths.length; i++) {
@@ -28,6 +28,7 @@ export function write_cell(
28
28
  if (!meta) {
29
29
  return false;
30
30
  }
31
+
31
32
  const type = model._schema[model._column_paths[meta.x!]];
32
33
  let text: string | number | boolean | null = active_cell.textContent || "";
33
34
  const id = model._ids[meta.y! - meta.y0][0];
@@ -36,12 +37,14 @@ export function write_cell(
36
37
  if (isNaN(parsed)) {
37
38
  return false;
38
39
  }
40
+
39
41
  text = parsed;
40
42
  } else if (type === "date" || type === "datetime") {
41
43
  const parsed = Date.parse(text);
42
44
  if (isNaN(parsed)) {
43
45
  return false;
44
46
  }
47
+
45
48
  text = parsed;
46
49
  } else if (type === "boolean") {
47
50
  text = text === "true" ? false : text === "false" ? true : null;
@@ -23,7 +23,10 @@ export function createDispatchClickListener(
23
23
  return async (event: Event): Promise<void> => {
24
24
  const mouseEvent = event as MouseEvent;
25
25
  const meta = table.getMeta(mouseEvent.target as HTMLElement);
26
- if (!meta || meta.type !== "body") return;
26
+ if (!meta || meta.type !== "body") {
27
+ return;
28
+ }
29
+
27
30
  const { x, y } = meta;
28
31
  const { row, column_names, config } = await getCellConfig(model, y, x);
29
32
  viewer.dispatchEvent(
@@ -18,7 +18,10 @@ export async function expandCollapseHandler(
18
18
  event: MouseEvent,
19
19
  ): Promise<void> {
20
20
  const meta = regularTable.getMeta(event.target as HTMLElement);
21
- if (!meta || meta.type !== "row_header") return;
21
+ if (!meta || meta.type !== "row_header") {
22
+ return;
23
+ }
24
+
22
25
  const is_collapse = (event.target as Element).classList.contains(
23
26
  "psp-tree-label-collapse",
24
27
  );
@@ -38,7 +38,9 @@ export function createMousedownListener(
38
38
  }
39
39
  }
40
40
 
41
- if (!target) return;
41
+ if (!target) {
42
+ return;
43
+ }
42
44
 
43
45
  if (target.classList.contains("psp-tree-label")) {
44
46
  if (model._edit_mode !== "SELECT_ROW_TREE") {
@@ -82,7 +84,9 @@ export function createDblclickListener(
82
84
  }
83
85
  }
84
86
 
85
- if (!target) return;
87
+ if (!target) {
88
+ return;
89
+ }
86
90
 
87
91
  if (target.classList.contains("psp-tree-label")) {
88
92
  if (model._edit_mode === "SELECT_ROW_TREE") {
@@ -107,7 +111,9 @@ export function createClickListener(regularTable: RegularTable): EventListener {
107
111
  }
108
112
  }
109
113
 
110
- if (!target) return;
114
+ if (!target) {
115
+ return;
116
+ }
111
117
 
112
118
  if (
113
119
  target.classList.contains("psp-tree-label") &&
@@ -68,7 +68,10 @@ function getPos(elem: ContentEditableElement): number {
68
68
  const _range = (elem.getRootNode() as Document)
69
69
  .getSelection()
70
70
  ?.getRangeAt(0);
71
- if (!_range) return 0;
71
+ if (!_range) {
72
+ return 0;
73
+ }
74
+
72
75
  const range = _range.cloneRange();
73
76
  range.selectNodeContents(elem);
74
77
  range.setEnd(_range.endContainer, _range.endOffset);
@@ -87,7 +90,10 @@ const moveSelection = lock(async function (
87
90
  dy: number,
88
91
  ): Promise<void> {
89
92
  const meta = table.getMeta(active_cell);
90
- if (!meta || meta.type !== "body") return;
93
+ if (!meta || meta.type !== "body") {
94
+ return;
95
+ }
96
+
91
97
  const num_columns = model._column_paths.length;
92
98
  const num_rows = model._num_rows;
93
99
  const selected_position = selected_position_map.get(table);
@@ -167,6 +173,7 @@ export function keydownListener(
167
173
  1,
168
174
  );
169
175
  }
176
+
170
177
  break;
171
178
  case "ArrowLeft":
172
179
  if (getPos(target as ContentEditableElement) === 0) {
@@ -180,6 +187,7 @@ export function keydownListener(
180
187
  0,
181
188
  );
182
189
  }
190
+
183
191
  break;
184
192
  case "ArrowUp":
185
193
  event.preventDefault();
@@ -200,6 +208,7 @@ export function keydownListener(
200
208
  0,
201
209
  );
202
210
  }
211
+
203
212
  break;
204
213
  case "ArrowDown":
205
214
  event.preventDefault();
@@ -85,7 +85,10 @@ const getMousedownListener =
85
85
  mouseEvent.button === 0 &&
86
86
  isSelectionMode(datagrid.model!._edit_mode)
87
87
  ) {
88
- if (isSingleClickMode(datagrid.model!._edit_mode)) return;
88
+ if (isSingleClickMode(datagrid.model!._edit_mode)) {
89
+ return;
90
+ }
91
+
89
92
  datagrid.model!._selection_state.CURRENT_MOUSEDOWN_COORDINATES = {};
90
93
  const meta = table.getMeta(mouseEvent.target as HTMLElement);
91
94
  if (
@@ -199,7 +202,9 @@ const getMouseupListener =
199
202
  const mode = datagrid.model!._edit_mode;
200
203
  if (isSelectionMode(mode)) {
201
204
  const meta = table.getMeta(mouseEvent.target as HTMLElement);
202
- if (!meta) return;
205
+ if (!meta) {
206
+ return;
207
+ }
203
208
 
204
209
  // For single-click modes (SELECT_ROW_TREE), handle toggle
205
210
  if (isSingleClickMode(mode)) {
@@ -423,11 +428,17 @@ const applyMouseAreaSelection = (
423
428
  className: string,
424
429
  ): void => {
425
430
  const predicate = SELECTION_PREDICATES[datagrid.model!._edit_mode];
426
- if (!predicate || selected.length === 0) return;
431
+ if (!predicate || selected.length === 0) {
432
+ return;
433
+ }
434
+
427
435
  const tds = table.querySelectorAll("tbody td");
428
436
  for (const td of tds) {
429
437
  const meta = table.getMeta(td as HTMLElement);
430
- if (!meta || meta.type !== "body") continue;
438
+ if (!meta || meta.type !== "body") {
439
+ continue;
440
+ }
441
+
431
442
  let rendered = false;
432
443
  for (const area of selected) {
433
444
  if (