@gradio/dataframe 0.17.0 → 0.17.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @gradio/dataframe
2
2
 
3
+ ## 0.17.1
4
+
5
+ ### Fixes
6
+
7
+ - [#10819](https://github.com/gradio-app/gradio/pull/10819) [`ac075ad`](https://github.com/gradio-app/gradio/commit/ac075ad6aaea855bfd4839cef7140719a584498e) - Fix cell menu not showing in non-editable dataframes. Thanks @hannahblair!
8
+
3
9
  ## 0.17.0
4
10
 
5
11
  ### Features
@@ -675,7 +675,7 @@
675
675
  />
676
676
 
677
677
  <Story
678
- name="Dataframe with sorting by multiple columns"
678
+ name="Non-interactive dataframe with sorting by multiple columns"
679
679
  args={{
680
680
  values: [
681
681
  [1, 2, 3],
@@ -685,7 +685,7 @@
685
685
  headers: ["A", "B", "C"],
686
686
  col_count: [3, "dynamic"],
687
687
  row_count: [3, "dynamic"],
688
- editable: true,
688
+ editable: false,
689
689
  sort_columns: [
690
690
  { col: 0, direction: "asc" },
691
691
  { col: 1, direction: "desc" }
@@ -708,7 +708,7 @@
708
708
  const cell_menu_button = canvas.getAllByLabelText("Open cell menu")[0];
709
709
  await userEvent.click(cell_menu_button);
710
710
 
711
- const sort_ascending_button = canvas.getByRole("button", {
711
+ const sort_ascending_button = canvas.getByRole("menuitem", {
712
712
  name: "Sort ascending"
713
713
  });
714
714
  await userEvent.click(sort_ascending_button);
@@ -719,7 +719,7 @@
719
719
  const cell_menu_button_2 = canvas.getAllByLabelText("Open cell menu")[1];
720
720
  await userEvent.click(cell_menu_button_2);
721
721
 
722
- const sort_descending_button = canvas.getByRole("button", {
722
+ const sort_descending_button = canvas.getByRole("menuitem", {
723
723
  name: "Sort descending"
724
724
  });
725
725
  await userEvent.click(sort_descending_button);
@@ -730,7 +730,7 @@
730
730
  const cell_menu_button_3 = canvas.getAllByLabelText("Open cell menu")[2];
731
731
  await userEvent.click(cell_menu_button_3);
732
732
 
733
- const sort_ascending_button_3 = canvas.getByRole("button", {
733
+ const sort_ascending_button_3 = canvas.getByRole("menuitem", {
734
734
  name: "Sort ascending"
735
735
  });
736
736
  await userEvent.click(sort_ascending_button_3);
@@ -19,14 +19,15 @@ export let on_clear_sort = () => {
19
19
  };
20
20
  export let sort_direction = null;
21
21
  export let sort_priority = null;
22
+ export let editable = true;
22
23
  export let i18n;
23
24
  let menu_element;
24
25
  $:
25
26
  is_header = row === -1;
26
27
  $:
27
- can_add_rows = row_count[1] === "dynamic";
28
+ can_add_rows = editable && row_count[1] === "dynamic";
28
29
  $:
29
- can_add_columns = col_count[1] === "dynamic";
30
+ can_add_columns = editable && col_count[1] === "dynamic";
30
31
  onMount(() => {
31
32
  position_menu();
32
33
  });
@@ -49,9 +50,10 @@ function position_menu() {
49
50
  }
50
51
  </script>
51
52
 
52
- <div bind:this={menu_element} class="cell-menu">
53
+ <div bind:this={menu_element} class="cell-menu" role="menu">
53
54
  {#if is_header}
54
55
  <button
56
+ role="menuitem"
55
57
  on:click={() => on_sort("asc")}
56
58
  class:active={sort_direction === "asc"}
57
59
  >
@@ -62,6 +64,7 @@ function position_menu() {
62
64
  {/if}
63
65
  </button>
64
66
  <button
67
+ role="menuitem"
65
68
  on:click={() => on_sort("desc")}
66
69
  class:active={sort_direction === "desc"}
67
70
  >
@@ -71,39 +74,65 @@ function position_menu() {
71
74
  <span class="priority">{sort_priority}</span>
72
75
  {/if}
73
76
  </button>
74
- <button on:click={on_clear_sort}>
77
+ <button role="menuitem" on:click={on_clear_sort}>
75
78
  <CellMenuIcons icon="clear-sort" />
76
79
  {i18n("dataframe.clear_sort")}
77
80
  </button>
78
81
  {/if}
79
82
 
80
83
  {#if !is_header && can_add_rows}
81
- <button on:click={() => on_add_row_above()}>
84
+ <button
85
+ role="menuitem"
86
+ on:click={() => on_add_row_above()}
87
+ aria-label="Add row above"
88
+ >
82
89
  <CellMenuIcons icon="add-row-above" />
83
90
  {i18n("dataframe.add_row_above")}
84
91
  </button>
85
- <button on:click={() => on_add_row_below()}>
92
+ <button
93
+ role="menuitem"
94
+ on:click={() => on_add_row_below()}
95
+ aria-label="Add row below"
96
+ >
86
97
  <CellMenuIcons icon="add-row-below" />
87
98
  {i18n("dataframe.add_row_below")}
88
99
  </button>
89
100
  {#if can_delete_rows}
90
- <button on:click={on_delete_row} class="delete">
101
+ <button
102
+ role="menuitem"
103
+ on:click={on_delete_row}
104
+ class="delete"
105
+ aria-label="Delete row"
106
+ >
91
107
  <CellMenuIcons icon="delete-row" />
92
108
  {i18n("dataframe.delete_row")}
93
109
  </button>
94
110
  {/if}
95
111
  {/if}
96
112
  {#if can_add_columns}
97
- <button on:click={() => on_add_column_left()}>
113
+ <button
114
+ role="menuitem"
115
+ on:click={() => on_add_column_left()}
116
+ aria-label="Add column to the left"
117
+ >
98
118
  <CellMenuIcons icon="add-column-left" />
99
119
  {i18n("dataframe.add_column_left")}
100
120
  </button>
101
- <button on:click={() => on_add_column_right()}>
121
+ <button
122
+ role="menuitem"
123
+ on:click={() => on_add_column_right()}
124
+ aria-label="Add column to the right"
125
+ >
102
126
  <CellMenuIcons icon="add-column-right" />
103
127
  {i18n("dataframe.add_column_right")}
104
128
  </button>
105
129
  {#if can_delete_cols}
106
- <button on:click={on_delete_col} class="delete">
130
+ <button
131
+ role="menuitem"
132
+ on:click={on_delete_col}
133
+ class="delete"
134
+ aria-label="Delete column"
135
+ >
107
136
  <CellMenuIcons icon="delete-column" />
108
137
  {i18n("dataframe.delete_column")}
109
138
  </button>
@@ -20,6 +20,7 @@ declare const __propDef: {
20
20
  on_clear_sort?: (() => void) | undefined;
21
21
  sort_direction?: (SortDirection | null) | undefined;
22
22
  sort_priority?: (number | null) | undefined;
23
+ editable?: boolean | undefined;
23
24
  i18n: I18nFormatter;
24
25
  };
25
26
  events: {
@@ -4,6 +4,7 @@
4
4
  <button
5
5
  aria-label="Open cell menu"
6
6
  class="cell-menu-button"
7
+ aria-haspopup="menu"
7
8
  on:click={on_click}
8
9
  on:touchstart={(event) => {
9
10
  event.preventDefault();
@@ -30,14 +30,14 @@ import {
30
30
  import {
31
31
  copy_table_data,
32
32
  get_max,
33
- handle_file_upload,
34
- sort_table_data
33
+ handle_file_upload
35
34
  } from "./utils/table_utils";
36
35
  import { make_headers, process_data } from "./utils/data_processing";
37
36
  import { handle_keydown } from "./utils/keyboard_utils";
38
37
  import {
39
38
  create_drag_handlers
40
39
  } from "./utils/drag_utils";
40
+ import { sort_data_and_preserve_selection } from "./utils/sort_utils";
41
41
  export let datatype;
42
42
  export let label = null;
43
43
  export let show_label = true;
@@ -137,8 +137,14 @@ let search_results = [[]];
137
137
  $:
138
138
  if (!dequal(values, old_val)) {
139
139
  if (parent) {
140
- for (let i = 0; i < 50; i++) {
141
- parent.style.removeProperty(`--cell-width-${i}`);
140
+ const is_reset2 = values.length === 0 || values.length === 1 && values[0].length === 0;
141
+ const is_different_structure2 = old_val !== void 0 && (values.length !== old_val.length || values[0] && old_val[0] && values[0].length !== old_val[0].length);
142
+ if (is_reset2 || is_different_structure2) {
143
+ for (let i = 0; i < 50; i++) {
144
+ parent.style.removeProperty(`--cell-width-${i}`);
145
+ }
146
+ last_width_data_length = 0;
147
+ last_width_column_count = 0;
142
148
  }
143
149
  }
144
150
  const is_reset = values.length === 0 || values.length === 1 && values[0].length === 0;
@@ -297,11 +303,19 @@ function handle_click_outside(event) {
297
303
  $:
298
304
  max = get_max(data);
299
305
  $:
300
- cells[0] && set_cell_widths();
306
+ cells[0] && cells[0]?.clientWidth && set_cell_widths();
301
307
  let cells = [];
302
308
  let parent;
303
309
  let table;
310
+ let last_width_data_length = 0;
311
+ let last_width_column_count = 0;
304
312
  function set_cell_widths() {
313
+ const column_count = data[0]?.length || 0;
314
+ if (last_width_data_length === data.length && last_width_column_count === column_count && $df_state.sort_state.sort_columns.length > 0) {
315
+ return;
316
+ }
317
+ last_width_data_length = data.length;
318
+ last_width_column_count = column_count;
305
319
  const widths = cells.map((el) => el?.clientWidth || 0);
306
320
  if (widths.length === 0)
307
321
  return;
@@ -332,21 +346,16 @@ function get_cell_width(index) {
332
346
  let table_height = values.slice(0, max_height / values.length * 37).length * 37 + 37;
333
347
  let scrollbar_width = 0;
334
348
  function sort_data(_data, _display_value, _styling) {
335
- let id = null;
336
- if (selected && selected[0] in _data && selected[1] in _data[selected[0]]) {
337
- id = _data[selected[0]][selected[1]].id;
338
- }
339
- sort_table_data(
349
+ const result = sort_data_and_preserve_selection(
340
350
  _data,
341
351
  _display_value,
342
352
  _styling,
343
- $df_state.sort_state.sort_columns
353
+ $df_state.sort_state.sort_columns,
354
+ selected,
355
+ get_current_indices
344
356
  );
345
- data = data;
346
- if (id) {
347
- const [i, j] = get_current_indices(id, data);
348
- selected = [i, j];
349
- }
357
+ data = result.data;
358
+ selected = result.selected;
350
359
  }
351
360
  $:
352
361
  sort_data(data, display_value, styling);
@@ -856,8 +865,9 @@ $:
856
865
  on_delete_row={() => delete_row_at(active_cell_menu?.row ?? -1)}
857
866
  on_delete_col={() =>
858
867
  delete_col_at(active_cell_menu?.col ?? active_header_menu?.col ?? -1)}
859
- can_delete_rows={!active_header_menu && data.length > 1}
860
- can_delete_cols={data.length > 0 && data[0]?.length > 1}
868
+ {editable}
869
+ can_delete_rows={!active_header_menu && data.length > 1 && editable}
870
+ can_delete_cols={data.length > 0 && data[0]?.length > 1 && editable}
861
871
  {i18n}
862
872
  on_sort={active_header_menu
863
873
  ? (direction) => {
@@ -118,7 +118,7 @@ function get_header_position(col_index) {
118
118
  <Padlock />
119
119
  {/if}
120
120
  </div>
121
- {#if editable && can_add_columns}
121
+ {#if can_add_columns}
122
122
  <CellMenuButton on_click={(event) => toggle_header_menu(event, i)} />
123
123
  {/if}
124
124
  </div>
@@ -11,3 +11,16 @@ export declare function sort_data(data: {
11
11
  col: number;
12
12
  direction: SortDirection;
13
13
  }[]): number[];
14
+ export declare function sort_data_and_preserve_selection(data: {
15
+ id: string;
16
+ value: string | number;
17
+ }[][], display_value: string[][] | null, styling: string[][] | null, sort_columns: {
18
+ col: number;
19
+ direction: SortDirection;
20
+ }[], selected: [number, number] | false, get_current_indices: (id: string, data: {
21
+ id: string;
22
+ value: string | number;
23
+ }[][]) => [number, number]): {
24
+ data: typeof data;
25
+ selected: [number, number] | false;
26
+ };
@@ -1,3 +1,4 @@
1
+ import { sort_table_data } from "./table_utils";
1
2
  export function get_sort_status(name, sort_columns, headers) {
2
3
  if (!sort_columns.length)
3
4
  return "none";
@@ -43,3 +44,16 @@ export function sort_data(data, sort_columns) {
43
44
  }
44
45
  return [...Array(data.length)].map((_, i) => i);
45
46
  }
47
+ export function sort_data_and_preserve_selection(data, display_value, styling, sort_columns, selected, get_current_indices) {
48
+ let id = null;
49
+ if (selected && selected[0] in data && selected[1] in data[selected[0]]) {
50
+ id = data[selected[0]][selected[1]].id;
51
+ }
52
+ sort_table_data(data, display_value, styling, sort_columns);
53
+ let new_selected = selected;
54
+ if (id) {
55
+ const [i, j] = get_current_indices(id, data);
56
+ new_selected = [i, j];
57
+ }
58
+ return { data, selected: new_selected };
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/dataframe",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -19,12 +19,12 @@
19
19
  "marked": "^12.0.0",
20
20
  "@gradio/atoms": "^0.14.0",
21
21
  "@gradio/button": "^0.4.10",
22
- "@gradio/markdown-code": "^0.4.1",
23
22
  "@gradio/client": "^1.13.1",
24
- "@gradio/statustracker": "^0.10.5",
23
+ "@gradio/icons": "^0.10.0",
25
24
  "@gradio/upload": "^0.15.5",
25
+ "@gradio/statustracker": "^0.10.5",
26
26
  "@gradio/utils": "^0.10.1",
27
- "@gradio/icons": "^0.10.0"
27
+ "@gradio/markdown-code": "^0.4.1"
28
28
  },
29
29
  "exports": {
30
30
  ".": {
@@ -21,13 +21,14 @@
21
21
  export let on_clear_sort: () => void = () => {};
22
22
  export let sort_direction: SortDirection | null = null;
23
23
  export let sort_priority: number | null = null;
24
+ export let editable = true;
24
25
 
25
26
  export let i18n: I18nFormatter;
26
27
  let menu_element: HTMLDivElement;
27
28
 
28
29
  $: is_header = row === -1;
29
- $: can_add_rows = row_count[1] === "dynamic";
30
- $: can_add_columns = col_count[1] === "dynamic";
30
+ $: can_add_rows = editable && row_count[1] === "dynamic";
31
+ $: can_add_columns = editable && col_count[1] === "dynamic";
31
32
 
32
33
  onMount(() => {
33
34
  position_menu();
@@ -56,9 +57,10 @@
56
57
  }
57
58
  </script>
58
59
 
59
- <div bind:this={menu_element} class="cell-menu">
60
+ <div bind:this={menu_element} class="cell-menu" role="menu">
60
61
  {#if is_header}
61
62
  <button
63
+ role="menuitem"
62
64
  on:click={() => on_sort("asc")}
63
65
  class:active={sort_direction === "asc"}
64
66
  >
@@ -69,6 +71,7 @@
69
71
  {/if}
70
72
  </button>
71
73
  <button
74
+ role="menuitem"
72
75
  on:click={() => on_sort("desc")}
73
76
  class:active={sort_direction === "desc"}
74
77
  >
@@ -78,39 +81,65 @@
78
81
  <span class="priority">{sort_priority}</span>
79
82
  {/if}
80
83
  </button>
81
- <button on:click={on_clear_sort}>
84
+ <button role="menuitem" on:click={on_clear_sort}>
82
85
  <CellMenuIcons icon="clear-sort" />
83
86
  {i18n("dataframe.clear_sort")}
84
87
  </button>
85
88
  {/if}
86
89
 
87
90
  {#if !is_header && can_add_rows}
88
- <button on:click={() => on_add_row_above()}>
91
+ <button
92
+ role="menuitem"
93
+ on:click={() => on_add_row_above()}
94
+ aria-label="Add row above"
95
+ >
89
96
  <CellMenuIcons icon="add-row-above" />
90
97
  {i18n("dataframe.add_row_above")}
91
98
  </button>
92
- <button on:click={() => on_add_row_below()}>
99
+ <button
100
+ role="menuitem"
101
+ on:click={() => on_add_row_below()}
102
+ aria-label="Add row below"
103
+ >
93
104
  <CellMenuIcons icon="add-row-below" />
94
105
  {i18n("dataframe.add_row_below")}
95
106
  </button>
96
107
  {#if can_delete_rows}
97
- <button on:click={on_delete_row} class="delete">
108
+ <button
109
+ role="menuitem"
110
+ on:click={on_delete_row}
111
+ class="delete"
112
+ aria-label="Delete row"
113
+ >
98
114
  <CellMenuIcons icon="delete-row" />
99
115
  {i18n("dataframe.delete_row")}
100
116
  </button>
101
117
  {/if}
102
118
  {/if}
103
119
  {#if can_add_columns}
104
- <button on:click={() => on_add_column_left()}>
120
+ <button
121
+ role="menuitem"
122
+ on:click={() => on_add_column_left()}
123
+ aria-label="Add column to the left"
124
+ >
105
125
  <CellMenuIcons icon="add-column-left" />
106
126
  {i18n("dataframe.add_column_left")}
107
127
  </button>
108
- <button on:click={() => on_add_column_right()}>
128
+ <button
129
+ role="menuitem"
130
+ on:click={() => on_add_column_right()}
131
+ aria-label="Add column to the right"
132
+ >
109
133
  <CellMenuIcons icon="add-column-right" />
110
134
  {i18n("dataframe.add_column_right")}
111
135
  </button>
112
136
  {#if can_delete_cols}
113
- <button on:click={on_delete_col} class="delete">
137
+ <button
138
+ role="menuitem"
139
+ on:click={on_delete_col}
140
+ class="delete"
141
+ aria-label="Delete column"
142
+ >
114
143
  <CellMenuIcons icon="delete-column" />
115
144
  {i18n("dataframe.delete_column")}
116
145
  </button>
@@ -5,6 +5,7 @@
5
5
  <button
6
6
  aria-label="Open cell menu"
7
7
  class="cell-menu-button"
8
+ aria-haspopup="menu"
8
9
  on:click={on_click}
9
10
  on:touchstart={(event) => {
10
11
  event.preventDefault();
@@ -38,8 +38,7 @@
38
38
  import {
39
39
  copy_table_data,
40
40
  get_max,
41
- handle_file_upload,
42
- sort_table_data
41
+ handle_file_upload
43
42
  } from "./utils/table_utils";
44
43
  import { make_headers, process_data } from "./utils/data_processing";
45
44
  import { handle_keydown } from "./utils/keyboard_utils";
@@ -48,6 +47,7 @@
48
47
  type DragState,
49
48
  type DragHandlers
50
49
  } from "./utils/drag_utils";
50
+ import { sort_data_and_preserve_selection } from "./utils/sort_utils";
51
51
 
52
52
  export let datatype: Datatype | Datatype[];
53
53
  export let label: string | null = null;
@@ -177,10 +177,23 @@
177
177
 
178
178
  $: if (!dequal(values, old_val)) {
179
179
  if (parent) {
180
- for (let i = 0; i < 50; i++) {
181
- parent.style.removeProperty(`--cell-width-${i}`);
180
+ // only clear column widths when the data structure changes
181
+ const is_reset =
182
+ values.length === 0 || (values.length === 1 && values[0].length === 0);
183
+ const is_different_structure =
184
+ old_val !== undefined &&
185
+ (values.length !== old_val.length ||
186
+ (values[0] && old_val[0] && values[0].length !== old_val[0].length));
187
+
188
+ if (is_reset || is_different_structure) {
189
+ for (let i = 0; i < 50; i++) {
190
+ parent.style.removeProperty(`--cell-width-${i}`);
191
+ }
192
+ last_width_data_length = 0;
193
+ last_width_column_count = 0;
182
194
  }
183
195
  }
196
+
184
197
  // only reset sort state when values are changed
185
198
  const is_reset =
186
199
  values.length === 0 || (values.length === 1 && values[0].length === 0);
@@ -366,12 +379,28 @@
366
379
 
367
380
  $: max = get_max(data);
368
381
 
369
- $: cells[0] && set_cell_widths();
382
+ // Modify how we trigger cell width calculations
383
+ // Only recalculate when cells actually change, not during sort
384
+ $: cells[0] && cells[0]?.clientWidth && set_cell_widths();
370
385
  let cells: HTMLTableCellElement[] = [];
371
386
  let parent: HTMLDivElement;
372
387
  let table: HTMLTableElement;
388
+ let last_width_data_length = 0;
389
+ let last_width_column_count = 0;
373
390
 
374
391
  function set_cell_widths(): void {
392
+ const column_count = data[0]?.length || 0;
393
+ if (
394
+ last_width_data_length === data.length &&
395
+ last_width_column_count === column_count &&
396
+ $df_state.sort_state.sort_columns.length > 0
397
+ ) {
398
+ return;
399
+ }
400
+
401
+ last_width_data_length = data.length;
402
+ last_width_column_count = column_count;
403
+
375
404
  const widths = cells.map((el) => el?.clientWidth || 0);
376
405
  if (widths.length === 0) return;
377
406
 
@@ -412,23 +441,17 @@
412
441
  _display_value: string[][] | null,
413
442
  _styling: string[][] | null
414
443
  ): void {
415
- let id = null;
416
- if (selected && selected[0] in _data && selected[1] in _data[selected[0]]) {
417
- id = _data[selected[0]][selected[1]].id;
418
- }
419
-
420
- sort_table_data(
444
+ const result = sort_data_and_preserve_selection(
421
445
  _data,
422
446
  _display_value,
423
447
  _styling,
424
- $df_state.sort_state.sort_columns
448
+ $df_state.sort_state.sort_columns,
449
+ selected,
450
+ get_current_indices
425
451
  );
426
- data = data;
427
452
 
428
- if (id) {
429
- const [i, j] = get_current_indices(id, data);
430
- selected = [i, j];
431
- }
453
+ data = result.data;
454
+ selected = result.selected;
432
455
  }
433
456
 
434
457
  $: sort_data(data, display_value, styling);
@@ -968,8 +991,9 @@
968
991
  on_delete_row={() => delete_row_at(active_cell_menu?.row ?? -1)}
969
992
  on_delete_col={() =>
970
993
  delete_col_at(active_cell_menu?.col ?? active_header_menu?.col ?? -1)}
971
- can_delete_rows={!active_header_menu && data.length > 1}
972
- can_delete_cols={data.length > 0 && data[0]?.length > 1}
994
+ {editable}
995
+ can_delete_rows={!active_header_menu && data.length > 1 && editable}
996
+ can_delete_cols={data.length > 0 && data[0]?.length > 1 && editable}
973
997
  {i18n}
974
998
  on_sort={active_header_menu
975
999
  ? (direction) => {
@@ -135,7 +135,7 @@
135
135
  <Padlock />
136
136
  {/if}
137
137
  </div>
138
- {#if editable && can_add_columns}
138
+ {#if can_add_columns}
139
139
  <CellMenuButton on_click={(event) => toggle_header_menu(event, i)} />
140
140
  {/if}
141
141
  </div>
@@ -1,4 +1,5 @@
1
1
  import type { Headers } from "../types";
2
+ import { sort_table_data } from "./table_utils";
2
3
 
3
4
  export type SortDirection = "asc" | "desc";
4
5
 
@@ -61,3 +62,30 @@ export function sort_data(
61
62
  }
62
63
  return [...Array(data.length)].map((_, i) => i);
63
64
  }
65
+
66
+ export function sort_data_and_preserve_selection(
67
+ data: { id: string; value: string | number }[][],
68
+ display_value: string[][] | null,
69
+ styling: string[][] | null,
70
+ sort_columns: { col: number; direction: SortDirection }[],
71
+ selected: [number, number] | false,
72
+ get_current_indices: (
73
+ id: string,
74
+ data: { id: string; value: string | number }[][]
75
+ ) => [number, number]
76
+ ): { data: typeof data; selected: [number, number] | false } {
77
+ let id = null;
78
+ if (selected && selected[0] in data && selected[1] in data[selected[0]]) {
79
+ id = data[selected[0]][selected[1]].id;
80
+ }
81
+
82
+ sort_table_data(data, display_value, styling, sort_columns);
83
+
84
+ let new_selected = selected;
85
+ if (id) {
86
+ const [i, j] = get_current_indices(id, data);
87
+ new_selected = [i, j];
88
+ }
89
+
90
+ return { data, selected: new_selected };
91
+ }