@perspective-dev/viewer-datagrid 4.3.0 → 4.4.1

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 (87) hide show
  1. package/dist/cdn/perspective-viewer-datagrid.js +4 -22
  2. package/dist/cdn/perspective-viewer-datagrid.js.map +4 -4
  3. package/dist/css/perspective-viewer-datagrid-toolbar.css +1 -1
  4. package/dist/css/perspective-viewer-datagrid.css +1 -1
  5. package/dist/esm/color_utils.d.ts +22 -0
  6. package/dist/esm/custom_elements/datagrid.d.ts +5 -5
  7. package/dist/esm/data_listener/format_cell.d.ts +1 -1
  8. package/dist/esm/data_listener/formatter_cache.d.ts +1 -1
  9. package/dist/esm/data_listener/index.d.ts +3 -2
  10. package/dist/esm/event_handlers/click/edit_click.d.ts +3 -2
  11. package/dist/esm/event_handlers/click.d.ts +4 -6
  12. package/dist/esm/event_handlers/dispatch_click.d.ts +3 -2
  13. package/dist/esm/event_handlers/expand_collapse.d.ts +1 -1
  14. package/dist/esm/event_handlers/focus.d.ts +4 -5
  15. package/dist/esm/event_handlers/header_click.d.ts +5 -3
  16. package/dist/esm/event_handlers/keydown/edit_keydown.d.ts +3 -4
  17. package/dist/esm/event_handlers/select_region.d.ts +3 -1
  18. package/dist/esm/event_handlers/sort.d.ts +8 -7
  19. package/dist/esm/model/create.d.ts +1 -1
  20. package/dist/esm/perspective-viewer-datagrid.js +3 -3
  21. package/dist/esm/perspective-viewer-datagrid.js.map +4 -4
  22. package/dist/esm/plugin/activate.d.ts +1 -1
  23. package/dist/esm/plugin/column_style_controls.d.ts +1 -1
  24. package/dist/esm/style_handlers/body.d.ts +3 -3
  25. package/dist/esm/style_handlers/column_header.d.ts +4 -3
  26. package/dist/esm/style_handlers/consolidated.d.ts +3 -47
  27. package/dist/esm/style_handlers/editable.d.ts +3 -2
  28. package/dist/esm/style_handlers/focus.d.ts +4 -4
  29. package/dist/esm/style_handlers/group_header.d.ts +1 -1
  30. package/dist/esm/style_handlers/table_cell/boolean.d.ts +1 -1
  31. package/dist/esm/style_handlers/table_cell/cell_flash.d.ts +1 -1
  32. package/dist/esm/style_handlers/table_cell/datetime.d.ts +6 -2
  33. package/dist/esm/style_handlers/table_cell/numeric.d.ts +1 -1
  34. package/dist/esm/style_handlers/table_cell/row_header.d.ts +1 -1
  35. package/dist/esm/style_handlers/table_cell/string.d.ts +1 -1
  36. package/dist/esm/style_handlers/types.d.ts +0 -4
  37. package/dist/esm/types.d.ts +10 -17
  38. package/package.json +3 -5
  39. package/src/css/mitered-headers.css +64 -0
  40. package/src/css/perspective-viewer-datagrid.css +6 -0
  41. package/src/{less/pro.less → css/pro.css} +32 -31
  42. package/src/css/regular_table.css +589 -0
  43. package/src/{less/row-hover.less → css/row-hover.css} +48 -29
  44. package/src/{less/scrollbar.less → css/scrollbar.css} +16 -15
  45. package/src/{less/sub-cell-scroll.less → css/sub-cell-scroll.css} +14 -13
  46. package/src/{less/toolbar.less → css/toolbar.css} +57 -39
  47. package/src/ts/color_utils.ts +144 -16
  48. package/src/ts/custom_elements/datagrid.ts +11 -12
  49. package/src/ts/custom_elements/toolbar.ts +4 -5
  50. package/src/ts/data_listener/format_cell.ts +28 -9
  51. package/src/ts/data_listener/formatter_cache.ts +1 -1
  52. package/src/ts/data_listener/index.ts +4 -8
  53. package/src/ts/event_handlers/click/edit_click.ts +7 -6
  54. package/src/ts/event_handlers/click.ts +39 -68
  55. package/src/ts/event_handlers/dispatch_click.ts +24 -25
  56. package/src/ts/event_handlers/expand_collapse.ts +7 -7
  57. package/src/ts/event_handlers/focus.ts +38 -35
  58. package/src/ts/event_handlers/header_click.ts +101 -62
  59. package/src/ts/event_handlers/keydown/edit_keydown.ts +49 -52
  60. package/src/ts/event_handlers/select_region.ts +144 -133
  61. package/src/ts/event_handlers/sort.ts +16 -24
  62. package/src/ts/model/column_overrides.ts +13 -4
  63. package/src/ts/model/create.ts +55 -59
  64. package/src/ts/model/toolbar.ts +23 -7
  65. package/src/ts/plugin/activate.ts +120 -92
  66. package/src/ts/plugin/column_style_controls.ts +1 -1
  67. package/src/ts/plugin/save.ts +1 -0
  68. package/src/ts/style_handlers/body.ts +56 -61
  69. package/src/ts/style_handlers/column_header.ts +16 -19
  70. package/src/ts/style_handlers/consolidated.ts +22 -123
  71. package/src/ts/style_handlers/editable.ts +10 -8
  72. package/src/ts/style_handlers/focus.ts +5 -5
  73. package/src/ts/style_handlers/group_header.ts +3 -2
  74. package/src/ts/style_handlers/table_cell/boolean.ts +3 -3
  75. package/src/ts/style_handlers/table_cell/cell_flash.ts +11 -11
  76. package/src/ts/style_handlers/table_cell/datetime.ts +14 -11
  77. package/src/ts/style_handlers/table_cell/numeric.ts +24 -25
  78. package/src/ts/style_handlers/table_cell/row_header.ts +2 -2
  79. package/src/ts/style_handlers/table_cell/string.ts +20 -18
  80. package/src/ts/style_handlers/types.ts +0 -10
  81. package/src/ts/types.ts +28 -20
  82. package/dist/esm/event_handlers/deselect_all.d.ts +0 -5
  83. package/dist/esm/event_handlers/row_select_click.d.ts +0 -4
  84. package/src/less/mitered-headers.less +0 -65
  85. package/src/less/regular_table.less +0 -526
  86. package/src/ts/event_handlers/deselect_all.ts +0 -28
  87. package/src/ts/event_handlers/row_select_click.ts +0 -92
@@ -14,25 +14,29 @@ import { focusSelectedCell } from "../../style_handlers/focus.js";
14
14
  import type {
15
15
  RegularTable,
16
16
  DatagridModel,
17
- PerspectiveViewerElement,
18
- SelectedPosition,
17
+ SelectedPositionMap,
19
18
  } from "../../types.js";
19
+ import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
20
20
 
21
- type SelectedPositionMap = Map<RegularTable, SelectedPosition>;
22
-
23
- type AsyncFunction<T extends unknown[], R> = (
24
- this: DatagridModel,
25
- ...args: T
26
- ) => Promise<R>;
21
+ type AsyncMoveFunction = (
22
+ model: DatagridModel,
23
+ table: RegularTable,
24
+ selected_position_map: SelectedPositionMap,
25
+ active_cell: HTMLElement,
26
+ dx: number,
27
+ dy: number,
28
+ ) => Promise<void | undefined>;
27
29
 
28
- function lock<T extends unknown[], R>(
29
- body: AsyncFunction<T, R>,
30
- ): AsyncFunction<T, R | undefined> {
30
+ function lock(body: AsyncMoveFunction): AsyncMoveFunction {
31
31
  let lockPromise: Promise<void> | undefined;
32
32
  return async function (
33
- this: DatagridModel,
34
- ...args: T
35
- ): Promise<R | undefined> {
33
+ model: DatagridModel,
34
+ table: RegularTable,
35
+ selected_position_map: SelectedPositionMap,
36
+ active_cell: HTMLElement,
37
+ dx: number,
38
+ dy: number,
39
+ ): Promise<void | undefined> {
36
40
  if (lockPromise) {
37
41
  await lockPromise;
38
42
  return;
@@ -40,7 +44,14 @@ function lock<T extends unknown[], R>(
40
44
 
41
45
  let resolve: () => void;
42
46
  lockPromise = new Promise((x) => (resolve = x));
43
- const result = await body.apply(this, args);
47
+ const result = await body(
48
+ model,
49
+ table,
50
+ selected_position_map,
51
+ active_cell,
52
+ dx,
53
+ dy,
54
+ );
44
55
  lockPromise = undefined;
45
56
  resolve!();
46
57
  return result;
@@ -52,23 +63,23 @@ interface ContentEditableElement extends HTMLElement {
52
63
  selectionStart?: number;
53
64
  }
54
65
 
55
- function getPos(this: ContentEditableElement): number {
56
- if (this.isContentEditable) {
57
- const _range = (this.getRootNode() as Document)
66
+ function getPos(elem: ContentEditableElement): number {
67
+ if (elem.isContentEditable) {
68
+ const _range = (elem.getRootNode() as Document)
58
69
  .getSelection()
59
70
  ?.getRangeAt(0);
60
71
  if (!_range) return 0;
61
72
  const range = _range.cloneRange();
62
- range.selectNodeContents(this);
73
+ range.selectNodeContents(elem);
63
74
  range.setEnd(_range.endContainer, _range.endOffset);
64
75
  return range.toString().length;
65
76
  } else {
66
- return this.selectionStart || 0;
77
+ return elem.selectionStart || 0;
67
78
  }
68
79
  }
69
80
 
70
81
  const moveSelection = lock(async function (
71
- this: DatagridModel,
82
+ model: DatagridModel,
72
83
  table: RegularTable,
73
84
  selected_position_map: SelectedPositionMap,
74
85
  active_cell: HTMLElement,
@@ -77,8 +88,8 @@ const moveSelection = lock(async function (
77
88
  ): Promise<void> {
78
89
  const meta = table.getMeta(active_cell);
79
90
  if (!meta || meta.type !== "body") return;
80
- const num_columns = this._column_paths.length;
81
- const num_rows = this._num_rows;
91
+ const num_columns = model._column_paths.length;
92
+ const num_rows = model._num_rows;
82
93
  const selected_position = selected_position_map.get(table);
83
94
  if (!selected_position) {
84
95
  return;
@@ -122,9 +133,9 @@ function isLastCell(
122
133
  }
123
134
 
124
135
  export function keydownListener(
125
- this: DatagridModel,
136
+ model: DatagridModel,
126
137
  table: RegularTable,
127
- _viewer: PerspectiveViewerElement,
138
+ _viewer: HTMLPerspectiveViewerElement,
128
139
  selected_position_map: SelectedPositionMap,
129
140
  event: KeyboardEvent,
130
141
  ): void {
@@ -134,12 +145,12 @@ export function keydownListener(
134
145
  switch (event.key) {
135
146
  case "Enter":
136
147
  event.preventDefault();
137
- if (isLastCell(this, table, target)) {
148
+ if (isLastCell(model, table, target)) {
138
149
  target.blur();
139
150
  selected_position_map.delete(table);
140
151
  } else if (event.shiftKey) {
141
- moveSelection.call(
142
- this,
152
+ moveSelection(
153
+ model,
143
154
  table,
144
155
  selected_position_map,
145
156
  target,
@@ -147,8 +158,8 @@ export function keydownListener(
147
158
  -1,
148
159
  );
149
160
  } else {
150
- moveSelection.call(
151
- this,
161
+ moveSelection(
162
+ model,
152
163
  table,
153
164
  selected_position_map,
154
165
  target,
@@ -158,10 +169,10 @@ export function keydownListener(
158
169
  }
159
170
  break;
160
171
  case "ArrowLeft":
161
- if (getPos.call(target as ContentEditableElement) === 0) {
172
+ if (getPos(target as ContentEditableElement) === 0) {
162
173
  event.preventDefault();
163
- moveSelection.call(
164
- this,
174
+ moveSelection(
175
+ model,
165
176
  table,
166
177
  selected_position_map,
167
178
  target,
@@ -172,23 +183,16 @@ export function keydownListener(
172
183
  break;
173
184
  case "ArrowUp":
174
185
  event.preventDefault();
175
- moveSelection.call(
176
- this,
177
- table,
178
- selected_position_map,
179
- target,
180
- 0,
181
- -1,
182
- );
186
+ moveSelection(model, table, selected_position_map, target, 0, -1);
183
187
  break;
184
188
  case "ArrowRight":
185
189
  if (
186
- getPos.call(target as ContentEditableElement) ===
190
+ getPos(target as ContentEditableElement) ===
187
191
  (target.textContent?.length || 0)
188
192
  ) {
189
193
  event.preventDefault();
190
- moveSelection.call(
191
- this,
194
+ moveSelection(
195
+ model,
192
196
  table,
193
197
  selected_position_map,
194
198
  target,
@@ -199,14 +203,7 @@ export function keydownListener(
199
203
  break;
200
204
  case "ArrowDown":
201
205
  event.preventDefault();
202
- moveSelection.call(
203
- this,
204
- table,
205
- selected_position_map,
206
- target,
207
- 0,
208
- 1,
209
- );
206
+ moveSelection(model, table, selected_position_map, target, 0, 1);
210
207
  break;
211
208
  default:
212
209
  }
@@ -13,16 +13,24 @@
13
13
  import { RegularTableElement } from "regular-table";
14
14
  import type {
15
15
  DatagridPluginElement,
16
- PerspectiveViewerElement,
16
+ EditMode,
17
17
  SelectionArea,
18
18
  } from "../types.js";
19
- import { ViewWindow } from "@perspective-dev/client";
19
+ import type { HTMLPerspectiveViewerElement } from "@perspective-dev/viewer";
20
+ import type { ViewWindow } from "@perspective-dev/client";
21
+ import type { CellMetadataBody } from "regular-table/dist/esm/types.js";
20
22
 
21
23
  const MOUSE_SELECTED_AREA_CLASS = "mouse-selected-area";
22
24
 
25
+ export type OnSelectCallback = (
26
+ area: SelectionArea,
27
+ isDeselect: boolean,
28
+ ) => void;
29
+
23
30
  interface AddAreaMouseSelectionOptions {
24
31
  className?: string;
25
32
  selected?: SelectionArea[];
33
+ onSelect?: OnSelectCallback;
26
34
  }
27
35
 
28
36
  export const addAreaMouseSelection = (
@@ -31,6 +39,7 @@ export const addAreaMouseSelection = (
31
39
  {
32
40
  className = MOUSE_SELECTED_AREA_CLASS,
33
41
  selected = [],
42
+ onSelect,
34
43
  }: AddAreaMouseSelectionOptions = {},
35
44
  ): RegularTableElement => {
36
45
  datagrid.model!._selection_state = {
@@ -50,7 +59,7 @@ export const addAreaMouseSelection = (
50
59
 
51
60
  table.addEventListener(
52
61
  "mouseup",
53
- getMouseupListener(datagrid, table, className),
62
+ getMouseupListener(datagrid, table, className, onSelect),
54
63
  );
55
64
 
56
65
  table.addStyleListener(() =>
@@ -60,6 +69,10 @@ export const addAreaMouseSelection = (
60
69
  return table;
61
70
  };
62
71
 
72
+ function isSingleClickMode(mode: EditMode): boolean {
73
+ return mode === "SELECT_ROW_TREE";
74
+ }
75
+
63
76
  const getMousedownListener =
64
77
  (
65
78
  datagrid: DatagridPluginElement,
@@ -70,10 +83,9 @@ const getMousedownListener =
70
83
  const mouseEvent = event as MouseEvent;
71
84
  if (
72
85
  mouseEvent.button === 0 &&
73
- (datagrid.model!._edit_mode === "SELECT_REGION" ||
74
- datagrid.model!._edit_mode === "SELECT_ROW" ||
75
- datagrid.model!._edit_mode === "SELECT_COLUMN")
86
+ isSelectionMode(datagrid.model!._edit_mode)
76
87
  ) {
88
+ if (isSingleClickMode(datagrid.model!._edit_mode)) return;
77
89
  datagrid.model!._selection_state.CURRENT_MOUSEDOWN_COORDINATES = {};
78
90
  const meta = table.getMeta(mouseEvent.target as HTMLElement);
79
91
  if (
@@ -122,11 +134,8 @@ const getMouseoverListener =
122
134
  ) =>
123
135
  (event: Event): void => {
124
136
  const mouseEvent = event as MouseEvent;
125
- if (
126
- datagrid.model!._edit_mode === "SELECT_REGION" ||
127
- datagrid.model!._edit_mode === "SELECT_ROW" ||
128
- datagrid.model!._edit_mode === "SELECT_COLUMN"
129
- ) {
137
+ const mode = datagrid.model!._edit_mode;
138
+ if (isSelectionMode(mode) && !isSingleClickMode(mode)) {
130
139
  if (
131
140
  datagrid.model!._selection_state
132
141
  .CURRENT_MOUSEDOWN_COORDINATES &&
@@ -183,17 +192,63 @@ const getMouseupListener =
183
192
  datagrid: DatagridPluginElement,
184
193
  table: RegularTableElement,
185
194
  className: string,
195
+ onSelect?: OnSelectCallback,
186
196
  ) =>
187
197
  (event: Event): void => {
188
198
  const mouseEvent = event as MouseEvent;
189
- if (
190
- datagrid.model!._edit_mode === "SELECT_REGION" ||
191
- datagrid.model!._edit_mode === "SELECT_ROW" ||
192
- datagrid.model!._edit_mode === "SELECT_COLUMN"
193
- ) {
199
+ const mode = datagrid.model!._edit_mode;
200
+ if (isSelectionMode(mode)) {
194
201
  const meta = table.getMeta(mouseEvent.target as HTMLElement);
195
202
  if (!meta) return;
196
203
 
204
+ // For single-click modes (SELECT_ROW_TREE), handle toggle
205
+ if (isSingleClickMode(mode)) {
206
+ if (
207
+ (meta.type === "body" || meta.type === "row_header") &&
208
+ meta.y !== undefined &&
209
+ meta.y >= 0
210
+ ) {
211
+ const existing =
212
+ datagrid.model!._selection_state.selected_areas;
213
+ const isSameRow =
214
+ existing.length > 0 && existing[0].y0 === meta.y;
215
+
216
+ if (isSameRow) {
217
+ // Deselect
218
+ datagrid.model!._selection_state.selected_areas = [];
219
+ datagrid.model!._selection_state.dirty = true;
220
+ applyMouseAreaSelections(
221
+ datagrid,
222
+ table,
223
+ className,
224
+ [],
225
+ );
226
+ onSelect?.(existing[0], true);
227
+ } else {
228
+ // Select new row
229
+ const area: SelectionArea = {
230
+ x0: 0,
231
+ x1: 0,
232
+ y0: meta.y,
233
+ y1: meta.y,
234
+ };
235
+ datagrid.model!._selection_state.selected_areas = [
236
+ area,
237
+ ];
238
+ datagrid.model!._selection_state.dirty = true;
239
+ applyMouseAreaSelections(datagrid, table, className);
240
+ onSelect?.(area, false);
241
+ }
242
+ }
243
+
244
+ datagrid.model!._selection_state.CURRENT_MOUSEDOWN_COORDINATES =
245
+ {};
246
+ datagrid.model!._selection_state.potential_selection =
247
+ undefined;
248
+ return;
249
+ }
250
+
251
+ // Drag-based modes (SELECT_ROW, SELECT_COLUMN, SELECT_REGION)
197
252
  if (
198
253
  (datagrid.model!._selection_state.old_selected_areas?.length ??
199
254
  0) > 0
@@ -260,80 +315,103 @@ const getMouseupListener =
260
315
  }
261
316
  };
262
317
 
318
+ function modeIncludesColumns(mode: EditMode): boolean {
319
+ return mode === "SELECT_COLUMN" || mode === "SELECT_REGION";
320
+ }
321
+
322
+ function modeIncludesRows(mode: EditMode): boolean {
323
+ return (
324
+ mode === "SELECT_ROW" ||
325
+ mode === "SELECT_REGION" ||
326
+ mode === "SELECT_ROW_TREE"
327
+ );
328
+ }
329
+
263
330
  function set_psp_selection(
264
- viewer: PerspectiveViewerElement,
331
+ viewer: HTMLPerspectiveViewerElement,
265
332
  datagrid: DatagridPluginElement,
266
333
  { x0, x1, y0, y1 }: SelectionArea,
267
334
  ): void {
268
335
  const viewport: ViewWindow = {};
269
336
  const mode = datagrid.model!._edit_mode;
270
- if (
271
- x0 !== undefined &&
272
- ["SELECT_COLUMN", "SELECT_REGION"].indexOf(mode) > -1
273
- ) {
337
+ if (x0 !== undefined && modeIncludesColumns(mode)) {
274
338
  viewport.start_col = x0;
275
339
  }
276
340
 
277
- if (
278
- x1 !== undefined &&
279
- ["SELECT_COLUMN", "SELECT_REGION"].indexOf(mode) > -1
280
- ) {
341
+ if (x1 !== undefined && modeIncludesColumns(mode)) {
281
342
  viewport.end_col = x1 + 1;
282
343
  }
283
344
 
284
- if (
285
- y0 !== undefined &&
286
- ["SELECT_ROW", "SELECT_REGION"].indexOf(mode) > -1
287
- ) {
345
+ if (y0 !== undefined && modeIncludesRows(mode)) {
288
346
  viewport.start_row = y0;
289
347
  }
290
348
 
291
- if (
292
- y1 !== undefined &&
293
- ["SELECT_ROW", "SELECT_REGION"].indexOf(mode) > -1
294
- ) {
349
+ if (y1 !== undefined && modeIncludesRows(mode)) {
295
350
  viewport.end_row = y1 + 1;
296
351
  }
297
352
 
298
353
  viewer.setSelection(viewport);
299
354
  }
300
355
 
356
+ type CellPredicate = (meta: CellMetadataBody, area: SelectionArea) => boolean;
357
+
358
+ const SELECTION_PREDICATES: Record<string, CellPredicate> = {
359
+ SELECT_REGION: (m, a) =>
360
+ a.x0 <= m.x && m.x <= a.x1 && a.y0 <= m.y && m.y <= a.y1,
361
+ SELECT_ROW: (m, a) => a.y0 <= m.y && m.y <= a.y1,
362
+ SELECT_ROW_TREE: (m, a) => a.y0 <= m.y && m.y <= a.y1,
363
+ SELECT_COLUMN: (m, a) => a.x0 <= m.x && m.x <= a.x1,
364
+ };
365
+
366
+ function isSelectionMode(mode: EditMode): boolean {
367
+ return (
368
+ mode === "SELECT_REGION" ||
369
+ mode === "SELECT_ROW" ||
370
+ mode === "SELECT_COLUMN" ||
371
+ mode === "SELECT_ROW_TREE"
372
+ );
373
+ }
374
+
301
375
  export const applyMouseAreaSelections = (
302
376
  datagrid: DatagridPluginElement,
303
377
  table: RegularTableElement,
304
378
  className: string,
305
379
  selected?: SelectionArea[],
306
380
  ): void => {
307
- if (
308
- datagrid.model!._edit_mode === "SELECT_REGION" ||
309
- datagrid.model!._edit_mode === "SELECT_ROW" ||
310
- datagrid.model!._edit_mode === "SELECT_COLUMN"
311
- ) {
381
+ const mode = datagrid.model!._edit_mode;
382
+ if (isSelectionMode(mode)) {
312
383
  selected = datagrid.model!._selection_state.selected_areas.slice(0);
313
384
  if (datagrid.model!._selection_state.potential_selection) {
314
385
  selected.push(datagrid.model!._selection_state.potential_selection);
315
386
  }
316
387
 
317
- const tds = table.querySelectorAll("tbody td");
318
-
319
388
  if (selected.length > 0) {
320
389
  set_psp_selection(
321
- datagrid.parentElement as any,
390
+ datagrid.parentElement as HTMLPerspectiveViewerElement,
322
391
  datagrid,
323
392
  selected[0],
324
393
  );
325
- applyMouseAreaSelection(datagrid, table, selected, className);
394
+
395
+ // SELECT_ROW_TREE styling is handled entirely by the
396
+ // identity-based system in body.ts, which styles both td
397
+ // and th uniformly in a single draw pass.
398
+ if (!isSingleClickMode(mode)) {
399
+ applyMouseAreaSelection(datagrid, table, selected, className);
400
+ }
326
401
  } else {
327
- (datagrid.parentElement as any).setSelection();
402
+ (
403
+ datagrid.parentElement as HTMLPerspectiveViewerElement
404
+ ).setSelection();
405
+ const tds = table.querySelectorAll("tbody td");
328
406
  for (const td of tds) {
329
407
  td.classList.remove(className);
330
408
  }
331
409
  }
332
410
  } else if (datagrid.model!._selection_state.dirty) {
333
411
  datagrid.model!._selection_state.dirty = false;
334
- const tds = table.querySelectorAll("tbody td");
335
- for (const td of tds) {
336
- td.classList.remove(className);
412
+ const cells = table.querySelectorAll("tbody td, tbody th");
413
+ for (const cell of cells) {
414
+ cell.classList.remove(className);
337
415
  }
338
416
  }
339
417
  };
@@ -344,96 +422,29 @@ const applyMouseAreaSelection = (
344
422
  selected: SelectionArea[],
345
423
  className: string,
346
424
  ): void => {
347
- if (datagrid.model!._edit_mode === "SELECT_REGION" && selected.length > 0) {
348
- const tds = table.querySelectorAll("tbody td");
349
-
350
- for (const td of tds) {
351
- const meta = table.getMeta(td as HTMLElement);
352
- if (!meta || meta.type !== "body") continue;
353
- let rendered = false;
354
- for (const { x0, x1, y0, y1 } of selected) {
355
- if (
356
- x0 !== undefined &&
357
- y0 !== undefined &&
358
- x1 !== undefined &&
359
- y1 !== undefined
360
- ) {
361
- if (
362
- x0 <= meta.x &&
363
- meta.x <= x1 &&
364
- y0 <= meta.y &&
365
- meta.y <= y1
366
- ) {
367
- rendered = true;
368
- datagrid.model!._selection_state.dirty = true;
369
- td.classList.add(className);
370
- }
371
- }
372
- }
373
-
374
- if (!rendered) {
375
- td.classList.remove(className);
376
- }
377
- }
378
- } else if (
379
- datagrid.model!._edit_mode === "SELECT_ROW" &&
380
- selected.length > 0
381
- ) {
382
- const tds = table.querySelectorAll("tbody td");
383
-
384
- for (const td of tds) {
385
- const meta = table.getMeta(td as HTMLElement);
386
- if (!meta) continue;
387
- let rendered = false;
388
- for (const { x0, x1, y0, y1 } of selected) {
389
- if (
390
- x0 !== undefined &&
391
- y0 !== undefined &&
392
- x1 !== undefined &&
393
- y1 !== undefined &&
394
- meta?.type === "body"
395
- ) {
396
- if (y0 <= meta.y && meta.y <= y1) {
397
- datagrid.model!._selection_state.dirty = true;
398
- rendered = true;
399
- td.classList.add(className);
400
- }
401
- }
402
- }
403
-
404
- if (!rendered) {
405
- td.classList.remove(className);
425
+ const predicate = SELECTION_PREDICATES[datagrid.model!._edit_mode];
426
+ if (!predicate || selected.length === 0) return;
427
+ const tds = table.querySelectorAll("tbody td");
428
+ for (const td of tds) {
429
+ const meta = table.getMeta(td as HTMLElement);
430
+ if (!meta || meta.type !== "body") continue;
431
+ let rendered = false;
432
+ for (const area of selected) {
433
+ if (
434
+ area.x0 !== undefined &&
435
+ area.y0 !== undefined &&
436
+ area.x1 !== undefined &&
437
+ area.y1 !== undefined &&
438
+ predicate(meta, area)
439
+ ) {
440
+ rendered = true;
441
+ datagrid.model!._selection_state.dirty = true;
442
+ td.classList.add(className);
406
443
  }
407
444
  }
408
- } else if (
409
- datagrid.model!._edit_mode === "SELECT_COLUMN" &&
410
- selected.length > 0
411
- ) {
412
- const tds = table.querySelectorAll("tbody td");
413
-
414
- for (const td of tds) {
415
- const meta = table.getMeta(td as HTMLElement);
416
- if (!meta) continue;
417
- let rendered = false;
418
- for (const { x0, x1, y0, y1 } of selected) {
419
- if (
420
- x0 !== undefined &&
421
- y0 !== undefined &&
422
- x1 !== undefined &&
423
- y1 !== undefined &&
424
- meta?.type === "body"
425
- ) {
426
- if (x0 <= meta.x && meta.x <= x1) {
427
- datagrid.model!._selection_state.dirty = true;
428
- rendered = true;
429
- td.classList.add(className);
430
- }
431
- }
432
- }
433
445
 
434
- if (!rendered) {
435
- td.classList.remove(className);
436
- }
446
+ if (!rendered) {
447
+ td.classList.remove(className);
437
448
  }
438
449
  }
439
450
  };