@gradio/dataframe 0.16.5 → 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.
Files changed (87) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/Dataframe.stories.svelte +202 -9
  3. package/Index.svelte +7 -13
  4. package/dist/Index.svelte +5 -9
  5. package/dist/Index.svelte.d.ts +9 -2
  6. package/dist/shared/CellMenu.svelte +91 -10
  7. package/dist/shared/CellMenu.svelte.d.ts +6 -0
  8. package/dist/shared/CellMenuButton.svelte +45 -0
  9. package/dist/shared/CellMenuButton.svelte.d.ts +16 -0
  10. package/dist/shared/CellMenuIcons.svelte +79 -0
  11. package/dist/shared/EditableCell.svelte +83 -14
  12. package/dist/shared/EditableCell.svelte.d.ts +12 -3
  13. package/dist/shared/EmptyRowButton.svelte +28 -0
  14. package/dist/shared/EmptyRowButton.svelte.d.ts +16 -0
  15. package/dist/shared/RowNumber.svelte +40 -0
  16. package/dist/shared/RowNumber.svelte.d.ts +17 -0
  17. package/dist/shared/Table.svelte +564 -1121
  18. package/dist/shared/Table.svelte.d.ts +4 -0
  19. package/dist/shared/TableCell.svelte +291 -0
  20. package/dist/shared/TableCell.svelte.d.ts +57 -0
  21. package/dist/shared/TableHeader.svelte +239 -0
  22. package/dist/shared/TableHeader.svelte.d.ts +45 -0
  23. package/dist/shared/Toolbar.svelte +18 -8
  24. package/dist/shared/VirtualTable.svelte +66 -19
  25. package/dist/shared/VirtualTable.svelte.d.ts +4 -0
  26. package/dist/shared/context/keyboard_context.d.ts +37 -0
  27. package/dist/shared/context/keyboard_context.js +12 -0
  28. package/dist/shared/context/selection_context.d.ts +32 -0
  29. package/dist/shared/context/selection_context.js +107 -0
  30. package/dist/shared/context/table_context.d.ts +141 -0
  31. package/dist/shared/context/table_context.js +375 -0
  32. package/dist/shared/icons/Padlock.svelte +24 -0
  33. package/dist/shared/icons/Padlock.svelte.d.ts +23 -0
  34. package/dist/shared/icons/SelectionButtons.svelte +85 -0
  35. package/dist/shared/icons/SelectionButtons.svelte.d.ts +18 -0
  36. package/dist/shared/icons/SortArrowDown.svelte +24 -0
  37. package/dist/shared/icons/SortArrowDown.svelte.d.ts +16 -0
  38. package/dist/shared/icons/SortArrowUp.svelte +24 -0
  39. package/dist/shared/icons/SortArrowUp.svelte.d.ts +16 -0
  40. package/dist/shared/icons/SortButtonDown.svelte +14 -0
  41. package/dist/shared/icons/SortButtonDown.svelte.d.ts +23 -0
  42. package/dist/shared/icons/SortButtonUp.svelte +15 -0
  43. package/dist/shared/icons/SortButtonUp.svelte.d.ts +23 -0
  44. package/dist/shared/icons/SortIcon.svelte +46 -68
  45. package/dist/shared/icons/SortIcon.svelte.d.ts +3 -2
  46. package/dist/shared/selection_utils.d.ts +2 -1
  47. package/dist/shared/selection_utils.js +39 -10
  48. package/dist/shared/utils/data_processing.d.ts +13 -0
  49. package/dist/shared/utils/data_processing.js +45 -0
  50. package/dist/shared/utils/drag_utils.d.ts +15 -0
  51. package/dist/shared/utils/drag_utils.js +57 -0
  52. package/dist/shared/utils/keyboard_utils.d.ts +2 -0
  53. package/dist/shared/utils/keyboard_utils.js +186 -0
  54. package/dist/shared/utils/sort_utils.d.ts +22 -3
  55. package/dist/shared/utils/sort_utils.js +44 -24
  56. package/dist/shared/utils/table_utils.d.ts +6 -5
  57. package/dist/shared/utils/table_utils.js +13 -56
  58. package/package.json +7 -7
  59. package/shared/CellMenu.svelte +90 -10
  60. package/shared/CellMenuButton.svelte +46 -0
  61. package/shared/CellMenuIcons.svelte +79 -0
  62. package/shared/EditableCell.svelte +97 -18
  63. package/shared/EmptyRowButton.svelte +29 -0
  64. package/shared/RowNumber.svelte +41 -0
  65. package/shared/Table.svelte +604 -1235
  66. package/shared/TableCell.svelte +324 -0
  67. package/shared/TableHeader.svelte +256 -0
  68. package/shared/Toolbar.svelte +19 -8
  69. package/shared/VirtualTable.svelte +72 -19
  70. package/shared/context/keyboard_context.ts +65 -0
  71. package/shared/context/selection_context.ts +168 -0
  72. package/shared/context/table_context.ts +625 -0
  73. package/shared/icons/Padlock.svelte +24 -0
  74. package/shared/icons/SelectionButtons.svelte +93 -0
  75. package/shared/icons/SortArrowDown.svelte +25 -0
  76. package/shared/icons/SortArrowUp.svelte +25 -0
  77. package/shared/icons/SortButtonDown.svelte +14 -0
  78. package/shared/icons/SortButtonUp.svelte +15 -0
  79. package/shared/icons/SortIcon.svelte +47 -70
  80. package/shared/selection_utils.ts +39 -13
  81. package/shared/utils/data_processing.ts +72 -0
  82. package/shared/utils/drag_utils.ts +92 -0
  83. package/shared/utils/keyboard_utils.ts +238 -0
  84. package/shared/utils/sort_utils.test.ts +262 -14
  85. package/shared/utils/sort_utils.ts +67 -31
  86. package/shared/utils/table_utils.test.ts +66 -45
  87. package/shared/utils/table_utils.ts +16 -86
@@ -1,32 +1,43 @@
1
+ <script context="module">import {
2
+ create_dataframe_context
3
+ } from "./context/table_context";
4
+ import { create_keyboard_context } from "./context/keyboard_context";
5
+ import { create_selection_context } from "./context/selection_context";
6
+ </script>
7
+
1
8
  <script>import { afterUpdate, createEventDispatcher, tick, onMount } from "svelte";
2
9
  import { dequal } from "dequal/lite";
3
10
  import { Upload } from "@gradio/upload";
4
11
  import EditableCell from "./EditableCell.svelte";
12
+ import RowNumber from "./RowNumber.svelte";
13
+ import TableHeader from "./TableHeader.svelte";
14
+ import TableCell from "./TableCell.svelte";
15
+ import EmptyRowButton from "./EmptyRowButton.svelte";
5
16
  import {} from "@gradio/client";
6
17
  import VirtualTable from "./VirtualTable.svelte";
7
18
  import CellMenu from "./CellMenu.svelte";
8
19
  import Toolbar from "./Toolbar.svelte";
9
- import SortIcon from "./icons/SortIcon.svelte";
10
20
  import {
11
21
  is_cell_selected,
12
- handle_selection,
13
- handle_delete_key,
14
22
  should_show_cell_menu,
15
23
  get_next_cell_coordinates,
16
24
  get_range_selection,
17
25
  move_cursor,
18
26
  get_current_indices,
19
27
  handle_click_outside as handle_click_outside_util,
20
- select_column,
21
- select_row,
22
28
  calculate_selection_positions
23
29
  } from "./selection_utils";
24
30
  import {
25
31
  copy_table_data,
26
32
  get_max,
27
- handle_file_upload,
28
- sort_table_data
33
+ handle_file_upload
29
34
  } from "./utils/table_utils";
35
+ import { make_headers, process_data } from "./utils/data_processing";
36
+ import { handle_keydown } from "./utils/keyboard_utils";
37
+ import {
38
+ create_drag_handlers
39
+ } from "./utils/drag_utils";
40
+ import { sort_data_and_preserve_selection } from "./utils/sort_utils";
30
41
  export let datatype;
31
42
  export let label = null;
32
43
  export let show_label = true;
@@ -35,6 +46,7 @@ export let values = [];
35
46
  export let col_count;
36
47
  export let row_count;
37
48
  export let latex_delimiters;
49
+ export let components = {};
38
50
  export let editable = true;
39
51
  export let wrap = false;
40
52
  export let root;
@@ -51,27 +63,50 @@ export let value_is_output = false;
51
63
  export let max_chars = void 0;
52
64
  export let show_search = "none";
53
65
  export let pinned_columns = 0;
54
- let actual_pinned_columns = 0;
66
+ export let static_columns = [];
55
67
  $:
56
68
  actual_pinned_columns = pinned_columns && data?.[0]?.length ? Math.min(pinned_columns, data[0].length) : 0;
57
- let selected_cells = [];
69
+ const { state: df_state, actions: df_actions } = create_dataframe_context({
70
+ show_fullscreen_button,
71
+ show_copy_button,
72
+ show_search,
73
+ show_row_numbers,
74
+ editable,
75
+ pinned_columns,
76
+ show_label,
77
+ line_breaks,
78
+ wrap,
79
+ max_height,
80
+ column_widths,
81
+ max_chars
82
+ });
58
83
  $:
59
- selected_cells = [...selected_cells];
60
- let selected = false;
84
+ selected_cells = $df_state.ui_state.selected_cells;
85
+ let previous_selected_cells = [];
86
+ $: {
87
+ if (copy_flash && !dequal(selected_cells, previous_selected_cells)) {
88
+ keyboard_ctx?.set_copy_flash(false);
89
+ }
90
+ previous_selected_cells = selected_cells;
91
+ }
61
92
  $:
62
- selected = selected_cells.length > 0 ? selected_cells[selected_cells.length - 1] : false;
93
+ selected = $df_state.ui_state.selected;
63
94
  export let display_value = null;
64
95
  export let styling = null;
65
- let t_rect;
66
96
  let els = {};
67
97
  let data_binding = {};
68
98
  const dispatch = createEventDispatcher();
69
- let editing = false;
99
+ $:
100
+ editing = $df_state.ui_state.editing;
70
101
  let clear_on_focus = false;
71
- let header_edit = false;
72
- let selected_header = false;
73
- let active_cell_menu = null;
74
- let active_header_menu = null;
102
+ $:
103
+ header_edit = $df_state.ui_state.header_edit;
104
+ $:
105
+ selected_header = $df_state.ui_state.selected_header;
106
+ $:
107
+ active_cell_menu = $df_state.ui_state.active_cell_menu;
108
+ $:
109
+ active_header_menu = $df_state.ui_state.active_header_menu;
75
110
  let is_fullscreen = false;
76
111
  let dragging = false;
77
112
  let copy_flash = false;
@@ -88,278 +123,135 @@ const get_data_at = (row, col) => data?.[row]?.[col]?.value;
88
123
  function make_id() {
89
124
  return Math.random().toString(36).substring(2, 15);
90
125
  }
91
- function make_headers(_head, col_count2, els2) {
92
- let _h = _head || [];
93
- if (col_count2[1] === "fixed" && _h.length < col_count2[0]) {
94
- const fill = Array(col_count2[0] - _h.length).fill("").map((_, i) => `${i + _h.length}`);
95
- _h = _h.concat(fill);
96
- }
97
- if (!_h || _h.length === 0) {
98
- return Array(col_count2[0]).fill(0).map((_, i) => {
99
- const _id = make_id();
100
- els2[_id] = { cell: null, input: null };
101
- return { id: _id, value: JSON.stringify(i + 1) };
102
- });
103
- }
104
- return _h.map((h, i) => {
105
- const _id = make_id();
106
- els2[_id] = { cell: null, input: null };
107
- return { id: _id, value: h ?? "" };
108
- });
109
- }
110
- function process_data(_values) {
111
- const data_row_length = _values.length;
112
- if (data_row_length === 0)
113
- return [];
114
- return Array(row_count[1] === "fixed" ? row_count[0] : data_row_length).fill(0).map((_, i) => {
115
- return Array(
116
- col_count[1] === "fixed" ? col_count[0] : _values[0].length || headers.length
117
- ).fill(0).map((_2, j) => {
118
- const id = make_id();
119
- els[id] = els[id] || { input: null, cell: null };
120
- const obj = { value: _values?.[i]?.[j] ?? "", id };
121
- data_binding[id] = obj;
122
- return obj;
123
- });
124
- });
125
- }
126
- let _headers = make_headers(headers, col_count, els);
126
+ let _headers = make_headers(headers, col_count, els, make_id);
127
127
  let old_headers = headers;
128
128
  $: {
129
129
  if (!dequal(headers, old_headers)) {
130
- _headers = make_headers(headers, col_count, els);
130
+ _headers = make_headers(headers, col_count, els, make_id);
131
131
  old_headers = JSON.parse(JSON.stringify(headers));
132
132
  }
133
133
  }
134
134
  let data = [[]];
135
135
  let old_val = void 0;
136
+ let search_results = [[]];
136
137
  $:
137
138
  if (!dequal(values, old_val)) {
138
- data = process_data(values);
139
- old_val = JSON.parse(JSON.stringify(values));
140
- }
141
- let previous_headers = _headers.map((h) => h.value);
142
- let previous_data = data.map((row) => row.map((cell) => String(cell.value)));
143
- async function trigger_change() {
144
- if (current_search_query)
145
- return;
146
- const current_headers = _headers.map((h) => h.value);
147
- const current_data = data.map(
148
- (row) => row.map((cell) => String(cell.value))
149
- );
150
- if (!dequal(current_data, previous_data) || !dequal(current_headers, previous_headers)) {
151
- dispatch("change", {
152
- data: data.map((row) => row.map((cell) => cell.value)),
153
- headers: _headers.map((h) => h.value),
154
- metadata: null
155
- // the metadata (display value, styling) cannot be changed by the user so we don't need to pass it up
156
- });
157
- if (!value_is_output) {
158
- dispatch("input");
159
- }
160
- previous_data = current_data;
161
- previous_headers = current_headers;
162
- }
163
- }
164
- function get_sort_status(name, _sort, direction) {
165
- if (!_sort)
166
- return "none";
167
- if (headers[_sort] === name) {
168
- if (direction === "asc")
169
- return "ascending";
170
- if (direction === "des")
171
- return "descending";
172
- }
173
- return "none";
174
- }
175
- async function handle_keydown(event) {
176
- if (selected_header !== false && header_edit === false) {
177
- switch (event.key) {
178
- case "ArrowDown":
179
- selected = [0, selected_header];
180
- selected_cells = [[0, selected_header]];
181
- selected_header = false;
182
- return;
183
- case "ArrowLeft":
184
- selected_header = selected_header > 0 ? selected_header - 1 : selected_header;
185
- return;
186
- case "ArrowRight":
187
- selected_header = selected_header < _headers.length - 1 ? selected_header + 1 : selected_header;
188
- return;
189
- case "Escape":
190
- event.preventDefault();
191
- selected_header = false;
192
- break;
193
- case "Enter":
194
- event.preventDefault();
195
- if (editable) {
196
- header_edit = selected_header;
139
+ if (parent) {
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}`);
197
145
  }
198
- break;
199
- }
200
- }
201
- if (event.key === "Delete" || event.key === "Backspace") {
202
- if (!editable)
203
- return;
204
- if (editing) {
205
- const [row, col] = editing;
206
- const input_el = els[data[row][col].id].input;
207
- if (input_el && input_el.selectionStart !== input_el.selectionEnd) {
208
- return;
209
- }
210
- if (event.key === "Delete" && input_el?.selectionStart !== input_el?.value.length) {
211
- return;
212
- }
213
- if (event.key === "Backspace" && input_el?.selectionStart !== 0) {
214
- return;
146
+ last_width_data_length = 0;
147
+ last_width_column_count = 0;
215
148
  }
216
149
  }
217
- event.preventDefault();
218
- if (selected_cells.length > 0) {
219
- data = handle_delete_key(data, selected_cells);
220
- dispatch("change", {
221
- data: data.map((row) => row.map((cell) => cell.value)),
222
- headers: _headers.map((h) => h.value),
223
- metadata: null
224
- });
225
- if (!value_is_output) {
226
- dispatch("input");
227
- }
150
+ const is_reset = values.length === 0 || values.length === 1 && values[0].length === 0;
151
+ const is_different_structure = old_val !== void 0 && (values.length !== old_val.length || values[0] && old_val[0] && values[0].length !== old_val[0].length);
152
+ data = process_data(
153
+ values,
154
+ els,
155
+ data_binding,
156
+ make_id,
157
+ display_value
158
+ );
159
+ old_val = JSON.parse(JSON.stringify(values));
160
+ if (is_reset || is_different_structure) {
161
+ df_actions.reset_sort_state();
162
+ } else if ($df_state.sort_state.sort_columns.length > 0) {
163
+ sort_data(data, display_value, styling);
228
164
  }
229
- return;
230
- }
231
- if (event.key === "c" && (event.metaKey || event.ctrlKey)) {
232
- event.preventDefault();
233
- if (selected_cells.length > 0) {
234
- await handle_copy();
165
+ if ($df_state.current_search_query) {
166
+ df_actions.handle_search(null);
167
+ }
168
+ if (parent && cells.length > 0) {
169
+ set_cell_widths();
235
170
  }
236
- return;
237
171
  }
238
- if (!selected) {
239
- return;
172
+ $:
173
+ if ($df_state.current_search_query !== void 0) {
174
+ const cell_map = /* @__PURE__ */ new Map();
175
+ data.forEach((row, row_idx) => {
176
+ row.forEach((cell, col_idx) => {
177
+ cell_map.set(cell.id, {
178
+ value: cell.value,
179
+ styling: styling?.[row_idx]?.[col_idx] || ""
180
+ });
181
+ });
182
+ });
183
+ const filtered = df_actions.filter_data(data);
184
+ search_results = filtered.map(
185
+ (row) => row.map((cell) => {
186
+ const original = cell_map.get(cell.id);
187
+ return {
188
+ ...cell,
189
+ display_value: original?.display_value || String(cell.value),
190
+ styling: original?.styling || ""
191
+ };
192
+ })
193
+ );
240
194
  }
241
- const [i, j] = selected;
242
- switch (event.key) {
243
- case "ArrowRight":
244
- case "ArrowLeft":
245
- case "ArrowDown":
246
- case "ArrowUp":
247
- if (editing)
248
- break;
249
- event.preventDefault();
250
- const next_coords = move_cursor(event.key, [i, j], data);
251
- if (next_coords) {
252
- if (event.shiftKey) {
253
- selected_cells = get_range_selection(
254
- selected_cells.length > 0 ? selected_cells[0] : [i, j],
255
- next_coords
256
- );
257
- editing = false;
258
- } else {
259
- selected_cells = [next_coords];
260
- editing = false;
261
- }
262
- selected = next_coords;
263
- } else if (next_coords === false && event.key === "ArrowUp" && i === 0) {
264
- selected_header = j;
265
- selected = false;
266
- selected_cells = [];
267
- editing = false;
268
- }
269
- break;
270
- case "Escape":
271
- if (!editable)
272
- break;
273
- event.preventDefault();
274
- editing = false;
275
- break;
276
- case "Enter":
277
- event.preventDefault();
278
- if (editable) {
279
- if (event.shiftKey) {
280
- add_row(i);
281
- await tick();
282
- selected = [i + 1, j];
283
- } else {
284
- if (dequal(editing, [i, j])) {
285
- const cell_id = data[i][j].id;
286
- const input_el = els[cell_id].input;
287
- if (input_el) {
288
- data[i][j].value = input_el.value;
289
- }
290
- editing = false;
291
- await tick();
292
- selected = [i, j];
293
- } else {
294
- editing = [i, j];
295
- clear_on_focus = false;
296
- }
297
- }
298
- }
299
- break;
300
- case "Tab":
301
- event.preventDefault();
302
- editing = false;
303
- const next_cell = get_next_cell_coordinates(
304
- [i, j],
305
- data,
306
- event.shiftKey
307
- );
308
- if (next_cell) {
309
- selected_cells = [next_cell];
310
- selected = next_cell;
311
- if (editable) {
312
- editing = next_cell;
313
- clear_on_focus = false;
314
- }
315
- }
316
- break;
317
- default:
318
- if (!editable)
319
- break;
320
- if ((!editing || editing && dequal(editing, [i, j])) && event.key.length === 1) {
321
- clear_on_focus = true;
322
- editing = [i, j];
323
- }
195
+ let previous_headers = _headers.map((h) => h.value);
196
+ let previous_data = data.map((row) => row.map((cell) => String(cell.value)));
197
+ $: {
198
+ if (data || _headers) {
199
+ df_actions.trigger_change(
200
+ data,
201
+ _headers,
202
+ previous_data,
203
+ previous_headers,
204
+ value_is_output,
205
+ dispatch
206
+ );
207
+ previous_data = data.map((row) => row.map((cell) => String(cell.value)));
208
+ previous_headers = _headers.map((h) => h.value);
324
209
  }
325
210
  }
326
- let sort_direction;
327
- let sort_by;
328
211
  function handle_sort(col, direction) {
329
- if (typeof sort_by !== "number" || sort_by !== col) {
330
- sort_direction = direction;
331
- sort_by = col;
332
- } else if (sort_by === col) {
333
- if (sort_direction === direction) {
334
- sort_direction = void 0;
335
- sort_by = void 0;
336
- } else {
337
- sort_direction = direction;
212
+ df_actions.handle_sort(col, direction);
213
+ sort_data(data, display_value, styling);
214
+ }
215
+ function clear_sort() {
216
+ df_actions.reset_sort_state();
217
+ sort_data(data, display_value, styling);
218
+ }
219
+ $: {
220
+ df_actions.sort_data(data, display_value, styling);
221
+ df_actions.update_row_order(data);
222
+ }
223
+ $: {
224
+ if ($df_state.sort_state.sort_columns) {
225
+ if ($df_state.sort_state.sort_columns.length > 0) {
226
+ sort_data(data, display_value, styling);
338
227
  }
339
228
  }
340
229
  }
341
230
  async function edit_header(i, _select = false) {
342
231
  if (!editable || header_edit === i)
343
232
  return;
344
- selected = false;
345
- selected_cells = [];
346
- selected_header = i;
347
- header_edit = i;
233
+ if (!editable || col_count[1] !== "dynamic" || header_edit === i)
234
+ return;
235
+ df_actions.set_header_edit(i);
236
+ }
237
+ function handle_header_click(event, col) {
238
+ if (event.target instanceof HTMLAnchorElement) {
239
+ return;
240
+ }
241
+ event.preventDefault();
242
+ event.stopPropagation();
243
+ if (!editable)
244
+ return;
245
+ clear_on_focus = false;
246
+ df_actions.set_editing(false);
247
+ df_actions.handle_header_click(col, editable);
248
+ parent.focus();
348
249
  }
349
250
  function end_header_edit(event) {
350
251
  if (!editable)
351
252
  return;
352
- switch (event.detail.key) {
353
- case "Escape":
354
- case "Enter":
355
- case "Tab":
356
- event.preventDefault();
357
- selected = false;
358
- selected_header = header_edit;
359
- header_edit = false;
360
- parent.focus();
361
- break;
362
- }
253
+ df_actions.end_header_edit(event.detail.key);
254
+ parent.focus();
363
255
  }
364
256
  async function add_row(index) {
365
257
  parent.focus();
@@ -380,84 +272,93 @@ async function add_row(index) {
380
272
  data = data;
381
273
  selected = [index !== void 0 ? index : data.length - 1, 0];
382
274
  }
383
- $:
384
- (data || _headers) && trigger_change();
385
275
  async function add_col(index) {
386
276
  parent.focus();
387
277
  if (col_count[1] !== "dynamic")
388
278
  return;
389
- const insert_index = index !== void 0 ? index : data[0].length;
390
- for (let i = 0; i < data.length; i++) {
391
- const _id = make_id();
392
- els[_id] = { cell: null, input: null };
393
- data[i].splice(insert_index, 0, { id: _id, value: "" });
394
- }
395
- headers.splice(insert_index, 0, `Header ${headers.length + 1}`);
396
- data = data;
397
- headers = headers;
279
+ const result = df_actions.add_col(data, headers, make_id, index);
280
+ result.data.forEach((row) => {
281
+ row.forEach((cell) => {
282
+ if (!els[cell.id]) {
283
+ els[cell.id] = { cell: null, input: null };
284
+ }
285
+ });
286
+ });
287
+ data = result.data;
288
+ headers = result.headers;
398
289
  await tick();
399
290
  requestAnimationFrame(() => {
400
- edit_header(insert_index, true);
291
+ edit_header(index !== void 0 ? index : data[0].length - 1, true);
401
292
  const new_w = parent.querySelectorAll("tbody")[1].offsetWidth;
402
293
  parent.querySelectorAll("table")[1].scrollTo({ left: new_w });
403
294
  });
404
295
  }
405
296
  function handle_click_outside(event) {
406
297
  if (handle_click_outside_util(event, parent)) {
407
- editing = false;
408
- selected_cells = [];
298
+ df_actions.clear_ui_state();
409
299
  header_edit = false;
410
300
  selected_header = false;
411
- active_cell_menu = null;
412
- active_header_menu = null;
413
301
  }
414
302
  }
415
303
  $:
416
304
  max = get_max(data);
417
305
  $:
418
- cells[0] && set_cell_widths();
306
+ cells[0] && cells[0]?.clientWidth && set_cell_widths();
419
307
  let cells = [];
420
308
  let parent;
421
309
  let table;
310
+ let last_width_data_length = 0;
311
+ let last_width_column_count = 0;
422
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;
423
319
  const widths = cells.map((el) => el?.clientWidth || 0);
424
320
  if (widths.length === 0)
425
321
  return;
426
322
  if (show_row_numbers) {
427
323
  parent.style.setProperty(`--cell-width-row-number`, `${widths[0]}px`);
428
324
  }
429
- const data_cells = show_row_numbers ? widths.slice(1) : widths;
430
- data_cells.forEach((width, i) => {
325
+ for (let i = 0; i < 50; i++) {
431
326
  if (!column_widths[i]) {
432
- parent.style.setProperty(
433
- `--cell-width-${i}`,
434
- `${width - scrollbar_width / data_cells.length}px`
435
- );
327
+ parent.style.removeProperty(`--cell-width-${i}`);
328
+ } else if (column_widths[i].endsWith("%")) {
329
+ const percentage = parseFloat(column_widths[i]);
330
+ const pixel_width = Math.floor(percentage / 100 * parent.clientWidth);
331
+ parent.style.setProperty(`--cell-width-${i}`, `${pixel_width}px`);
332
+ } else {
333
+ parent.style.setProperty(`--cell-width-${i}`, column_widths[i]);
334
+ }
335
+ }
336
+ widths.forEach((width, i) => {
337
+ if (!column_widths[i]) {
338
+ const calculated_width = `${Math.max(width, 45)}px`;
339
+ parent.style.setProperty(`--cell-width-${i}`, calculated_width);
436
340
  }
437
341
  });
438
342
  }
439
343
  function get_cell_width(index) {
440
- return column_widths[index] || `var(--cell-width-${index})`;
344
+ return `var(--cell-width-${index})`;
441
345
  }
442
346
  let table_height = values.slice(0, max_height / values.length * 37).length * 37 + 37;
443
347
  let scrollbar_width = 0;
444
- function sort_data(_data, _display_value, _styling, col, dir) {
445
- let id = null;
446
- if (selected && selected[0] in _data && selected[1] in _data[selected[0]]) {
447
- id = _data[selected[0]][selected[1]].id;
448
- }
449
- if (typeof col !== "number" || !dir) {
450
- return;
451
- }
452
- sort_table_data(_data, _display_value, _styling, col, dir);
453
- data = data;
454
- if (id) {
455
- const [i, j] = get_current_indices(id, data);
456
- selected = [i, j];
457
- }
348
+ function sort_data(_data, _display_value, _styling) {
349
+ const result = sort_data_and_preserve_selection(
350
+ _data,
351
+ _display_value,
352
+ _styling,
353
+ $df_state.sort_state.sort_columns,
354
+ selected,
355
+ get_current_indices
356
+ );
357
+ data = result.data;
358
+ selected = result.selected;
458
359
  }
459
360
  $:
460
- sort_data(data, display_value, styling, sort_by, sort_direction);
361
+ sort_data(data, display_value, styling);
461
362
  $:
462
363
  selected_index = !!selected && selected[0];
463
364
  let is_visible = false;
@@ -485,80 +386,58 @@ onMount(() => {
485
386
  );
486
387
  };
487
388
  });
488
- function handle_cell_click(event, row, col) {
489
- if (event.target instanceof HTMLAnchorElement) {
490
- return;
491
- }
492
- event.preventDefault();
493
- event.stopPropagation();
494
- if (show_row_numbers && col === -1)
495
- return;
496
- clear_on_focus = false;
497
- active_cell_menu = null;
498
- active_header_menu = null;
499
- selected_header = false;
500
- header_edit = false;
501
- selected_cells = handle_selection([row, col], selected_cells, event);
502
- parent.focus();
503
- if (editable) {
504
- if (selected_cells.length === 1) {
505
- editing = [row, col];
506
- tick().then(() => {
507
- const input_el = els[data[row][col].id].input;
508
- if (input_el) {
509
- input_el.focus();
510
- input_el.selectionStart = input_el.selectionEnd = input_el.value.length;
511
- }
512
- });
513
- } else {
514
- editing = false;
389
+ $:
390
+ keyboard_ctx = create_keyboard_context({
391
+ selected_header,
392
+ header_edit,
393
+ editing,
394
+ selected,
395
+ selected_cells,
396
+ editable,
397
+ data,
398
+ headers: _headers,
399
+ els,
400
+ df_actions,
401
+ dispatch,
402
+ add_row,
403
+ get_next_cell_coordinates,
404
+ get_range_selection,
405
+ move_cursor,
406
+ copy_flash,
407
+ parent_element: parent,
408
+ set_copy_flash: (value) => {
409
+ copy_flash = value;
410
+ if (value) {
411
+ setTimeout(() => {
412
+ copy_flash = false;
413
+ }, 800);
414
+ }
515
415
  }
516
- }
517
- toggle_cell_button(row, col);
518
- dispatch("select", {
519
- index: [row, col],
520
- value: get_data_at(row, col),
521
- row_value: data[row].map((d) => d.value)
522
416
  });
417
+ $:
418
+ selection_ctx = create_selection_context({
419
+ df_actions,
420
+ dispatch,
421
+ data,
422
+ els,
423
+ editable,
424
+ show_row_numbers,
425
+ get_data_at,
426
+ clear_on_focus,
427
+ selected_cells,
428
+ parent_element: parent
429
+ });
430
+ function handle_cell_click(event, row, col) {
431
+ selection_ctx.actions.handle_cell_click(event, row, col);
523
432
  }
524
433
  function toggle_cell_menu(event, row, col) {
525
- event.stopPropagation();
526
- if (active_cell_menu && active_cell_menu.row === row && active_cell_menu.col === col) {
527
- active_cell_menu = null;
528
- } else {
529
- const cell = event.target.closest("td");
530
- if (cell) {
531
- const rect = cell.getBoundingClientRect();
532
- active_cell_menu = { row, col, x: rect.right, y: rect.bottom };
533
- }
534
- }
535
- }
536
- function add_row_at(index, position) {
537
- const row_index = position === "above" ? index : index + 1;
538
- add_row(row_index);
539
- active_cell_menu = null;
540
- active_header_menu = null;
541
- }
542
- function add_col_at(index, position) {
543
- const col_index = position === "left" ? index : index + 1;
544
- add_col(col_index);
545
- active_cell_menu = null;
546
- active_header_menu = null;
434
+ selection_ctx.actions.toggle_cell_menu(event, row, col);
547
435
  }
548
- function handle_resize() {
549
- active_cell_menu = null;
550
- active_header_menu = null;
551
- selected_cells = [];
552
- selected = false;
553
- editing = false;
554
- set_cell_widths();
555
- }
556
- let active_button = null;
557
- function toggle_header_button(col) {
558
- active_button = active_button?.type === "header" && active_button.col === col ? null : { type: "header", col };
436
+ function handle_select_column(col) {
437
+ selection_ctx.actions.handle_select_column(col);
559
438
  }
560
- function toggle_cell_button(row, col) {
561
- active_button = active_button?.type === "cell" && active_button.row === row && active_button.col === col ? null : { type: "cell", row, col };
439
+ function handle_select_row(row) {
440
+ selection_ctx.actions.handle_select_row(row);
562
441
  }
563
442
  function toggle_fullscreen() {
564
443
  if (!document.fullscreenElement) {
@@ -572,93 +451,44 @@ function toggle_fullscreen() {
572
451
  function handle_fullscreen_change() {
573
452
  is_fullscreen = !!document.fullscreenElement;
574
453
  }
575
- async function handle_copy() {
576
- await copy_table_data(data, selected_cells);
577
- copy_flash = true;
578
- setTimeout(() => {
579
- copy_flash = false;
580
- }, 800);
581
- }
582
454
  function toggle_header_menu(event, col) {
583
455
  event.stopPropagation();
584
456
  if (active_header_menu && active_header_menu.col === col) {
585
- active_header_menu = null;
457
+ df_actions.set_active_header_menu(null);
586
458
  } else {
587
459
  const header = event.target.closest("th");
588
460
  if (header) {
589
461
  const rect = header.getBoundingClientRect();
590
- active_header_menu = { col, x: rect.right, y: rect.bottom };
462
+ df_actions.set_active_header_menu({
463
+ col,
464
+ x: rect.right,
465
+ y: rect.bottom
466
+ });
591
467
  }
592
468
  }
593
469
  }
594
470
  afterUpdate(() => {
595
471
  value_is_output = false;
596
472
  });
597
- async function delete_row(index) {
598
- parent.focus();
599
- if (row_count[1] !== "dynamic")
600
- return;
601
- if (data.length <= 1)
602
- return;
603
- data.splice(index, 1);
604
- data = data;
605
- selected = false;
606
- }
607
- async function delete_col(index) {
608
- parent.focus();
473
+ function delete_col_at(index) {
609
474
  if (col_count[1] !== "dynamic")
610
475
  return;
611
- if (_headers.length <= 1)
476
+ if (data[0].length <= 1)
612
477
  return;
613
- _headers.splice(index, 1);
614
- _headers = _headers;
615
- if (data.length > 0) {
616
- data.forEach((row) => {
617
- row.splice(index, 1);
618
- });
619
- data = data;
620
- }
621
- selected = false;
478
+ const result = df_actions.delete_col_at(data, headers, index);
479
+ data = result.data;
480
+ headers = result.headers;
481
+ _headers = make_headers(headers, col_count, els, make_id);
482
+ df_actions.set_active_cell_menu(null);
483
+ df_actions.set_active_header_menu(null);
484
+ df_actions.set_selected(false);
485
+ df_actions.set_selected_cells([]);
486
+ df_actions.set_editing(false);
622
487
  }
623
488
  function delete_row_at(index) {
624
- delete_row(index);
625
- active_cell_menu = null;
626
- active_header_menu = null;
627
- }
628
- function delete_col_at(index) {
629
- delete_col(index);
630
- active_cell_menu = null;
631
- active_header_menu = null;
632
- }
633
- let row_order = [];
634
- $: {
635
- if (typeof sort_by === "number" && sort_direction && sort_by >= 0 && sort_by < data[0].length) {
636
- const indices = [...Array(data.length)].map((_, i) => i);
637
- const sort_index = sort_by;
638
- indices.sort((a, b) => {
639
- const row_a = data[a];
640
- const row_b = data[b];
641
- if (!row_a || !row_b || sort_index >= row_a.length || sort_index >= row_b.length)
642
- return 0;
643
- const val_a = row_a[sort_index].value;
644
- const val_b = row_b[sort_index].value;
645
- const comp = val_a < val_b ? -1 : val_a > val_b ? 1 : 0;
646
- return sort_direction === "asc" ? comp : -comp;
647
- });
648
- row_order = indices;
649
- } else {
650
- row_order = [...Array(data.length)].map((_, i) => i);
651
- }
652
- }
653
- function handle_select_column(col) {
654
- selected_cells = select_column(data, col);
655
- selected = selected_cells[0];
656
- editing = false;
657
- }
658
- function handle_select_row(row) {
659
- selected_cells = select_row(data, row);
660
- selected = selected_cells[0];
661
- editing = false;
489
+ data = df_actions.delete_row_at(data, index);
490
+ df_actions.set_active_cell_menu(null);
491
+ df_actions.set_active_header_menu(null);
662
492
  }
663
493
  let coords;
664
494
  $:
@@ -677,48 +507,111 @@ $:
677
507
  "--selected-col-pos",
678
508
  positions.col_pos
679
509
  );
680
- if (positions.row_pos) {
681
- document.documentElement.style.setProperty(
682
- "--selected-row-pos",
683
- positions.row_pos
684
- );
685
- }
510
+ document.documentElement.style.setProperty(
511
+ "--selected-row-pos",
512
+ positions.row_pos || "0px"
513
+ );
686
514
  }
687
- let current_search_query = null;
688
- function handle_search(search_query) {
689
- current_search_query = search_query;
690
- dispatch("search", search_query);
691
- }
692
515
  function commit_filter() {
693
- if (current_search_query && show_search === "filter") {
694
- dispatch("change", {
695
- data: data.map((row) => row.map((cell) => cell.value)),
696
- headers: _headers.map((h) => h.value),
697
- metadata: null
516
+ if ($df_state.current_search_query && show_search === "filter") {
517
+ const filtered_data = [];
518
+ const filtered_display_values = [];
519
+ const filtered_styling = [];
520
+ search_results.forEach((row) => {
521
+ const data_row = [];
522
+ const display_row = [];
523
+ const styling_row = [];
524
+ row.forEach((cell) => {
525
+ data_row.push(cell.value);
526
+ display_row.push(cell.display_value || String(cell.value));
527
+ styling_row.push(cell.styling || "");
528
+ });
529
+ filtered_data.push(data_row);
530
+ filtered_display_values.push(display_row);
531
+ filtered_styling.push(styling_row);
698
532
  });
533
+ const change_payload = {
534
+ data: filtered_data,
535
+ headers: _headers.map((h) => h.value),
536
+ metadata: {
537
+ display_value: filtered_display_values,
538
+ styling: filtered_styling
539
+ }
540
+ };
541
+ dispatch("change", change_payload);
699
542
  if (!value_is_output) {
700
543
  dispatch("input");
701
544
  }
702
- current_search_query = null;
545
+ df_actions.handle_search(null);
703
546
  }
704
547
  }
705
- function handle_header_click(event, col) {
706
- if (event.target instanceof HTMLAnchorElement) {
707
- return;
708
- }
709
- event.preventDefault();
710
- event.stopPropagation();
711
- if (!editable)
712
- return;
713
- clear_on_focus = false;
548
+ let viewport;
549
+ let show_scroll_button = false;
550
+ function scroll_to_top() {
551
+ viewport.scrollTo({
552
+ top: 0
553
+ });
554
+ }
555
+ function handle_resize() {
556
+ df_actions.set_active_cell_menu(null);
557
+ df_actions.set_active_header_menu(null);
558
+ selected_cells = [];
559
+ selected = false;
560
+ editing = false;
561
+ set_cell_widths();
562
+ }
563
+ function add_row_at(index, position) {
564
+ const row_index = position === "above" ? index : index + 1;
565
+ add_row(row_index);
714
566
  active_cell_menu = null;
715
567
  active_header_menu = null;
716
- selected = false;
717
- selected_cells = [];
718
- selected_header = col;
719
- header_edit = col;
720
- parent.focus();
721
568
  }
569
+ function add_col_at(index, position) {
570
+ const col_index = position === "left" ? index : index + 1;
571
+ add_col(col_index);
572
+ active_cell_menu = null;
573
+ active_header_menu = null;
574
+ }
575
+ export function reset_sort_state() {
576
+ df_actions.reset_sort_state();
577
+ }
578
+ let is_dragging = false;
579
+ let drag_start = null;
580
+ let mouse_down_pos = null;
581
+ const drag_state = {
582
+ is_dragging,
583
+ drag_start,
584
+ mouse_down_pos
585
+ };
586
+ $: {
587
+ is_dragging = drag_state.is_dragging;
588
+ drag_start = drag_state.drag_start;
589
+ mouse_down_pos = drag_state.mouse_down_pos;
590
+ }
591
+ let drag_handlers;
592
+ function init_drag_handlers() {
593
+ drag_handlers = create_drag_handlers(
594
+ drag_state,
595
+ (value) => is_dragging = value,
596
+ (cells2) => df_actions.set_selected_cells(cells2),
597
+ (cell) => df_actions.set_selected(cell),
598
+ (event, row, col) => selection_ctx.actions.handle_cell_click(event, row, col),
599
+ show_row_numbers,
600
+ parent
601
+ );
602
+ }
603
+ $:
604
+ if (parent)
605
+ init_drag_handlers();
606
+ $:
607
+ handle_mouse_down = drag_handlers?.handle_mouse_down || (() => {
608
+ });
609
+ $:
610
+ handle_mouse_move = drag_handlers?.handle_mouse_move || (() => {
611
+ });
612
+ $:
613
+ handle_mouse_up = drag_handlers?.handle_mouse_up || (() => {
614
+ });
722
615
  </script>
723
616
 
724
617
  <svelte:window on:resize={() => set_cell_widths()} />
@@ -735,140 +628,70 @@ function handle_header_click(event, col) {
735
628
  {show_fullscreen_button}
736
629
  {is_fullscreen}
737
630
  on:click={toggle_fullscreen}
738
- on_copy={handle_copy}
631
+ on_copy={async () => await copy_table_data(data, null)}
739
632
  {show_copy_button}
740
633
  {show_search}
741
- on:search={(e) => handle_search(e.detail)}
634
+ on:search={(e) => df_actions.handle_search(e.detail)}
742
635
  on_commit_filter={commit_filter}
743
- {current_search_query}
636
+ current_search_query={$df_state.current_search_query}
744
637
  />
745
638
  </div>
746
639
  {/if}
747
640
  <div
748
641
  bind:this={parent}
749
642
  class="table-wrap"
750
- class:dragging
643
+ class:dragging={is_dragging}
751
644
  class:no-wrap={!wrap}
752
645
  style="height:{table_height}px;"
753
646
  class:menu-open={active_cell_menu || active_header_menu}
754
- on:keydown={(e) => handle_keydown(e)}
647
+ on:keydown={(e) => handle_keydown(e, keyboard_ctx)}
648
+ on:mousemove={handle_mouse_move}
649
+ on:mouseup={handle_mouse_up}
650
+ on:mouseleave={handle_mouse_up}
755
651
  role="grid"
756
652
  tabindex="0"
757
653
  >
758
- {#if selected !== false && selected_cells.length === 1}
759
- <button
760
- class="selection-button selection-button-column"
761
- on:click|stopPropagation={() => handle_select_column(coords[1])}
762
- aria-label="Select column"
763
- >
764
- &#8942;
765
- </button>
766
- <button
767
- class="selection-button selection-button-row"
768
- on:click|stopPropagation={() => handle_select_row(coords[0])}
769
- aria-label="Select row"
770
- >
771
- &#8942;
772
- </button>
773
- {/if}
774
- <table
775
- bind:contentRect={t_rect}
776
- bind:this={table}
777
- class:fixed-layout={column_widths.length != 0}
778
- >
654
+ <table bind:this={table}>
779
655
  {#if label && label.length !== 0}
780
656
  <caption class="sr-only">{label}</caption>
781
657
  {/if}
782
658
  <thead>
783
659
  <tr>
784
660
  {#if show_row_numbers}
785
- <th
786
- class="row-number-header frozen-column always-frozen"
787
- style="left: 0;"
788
- >
789
- <div class="cell-wrap">
790
- <div class="header-content">
791
- <div class="header-text"></div>
792
- </div>
793
- </div>
794
- </th>
661
+ <RowNumber is_header={true} />
795
662
  {/if}
796
663
  {#each _headers as { value, id }, i (id)}
797
- <th
798
- class:frozen-column={i < actual_pinned_columns}
799
- class:last-frozen={i === actual_pinned_columns - 1}
800
- class:focus={header_edit === i || selected_header === i}
801
- aria-sort={get_sort_status(value, sort_by, sort_direction)}
802
- style="width: {get_cell_width(i)}; left: {i <
803
- actual_pinned_columns
804
- ? i === 0
805
- ? show_row_numbers
806
- ? 'var(--cell-width-row-number)'
807
- : '0'
808
- : `calc(${show_row_numbers ? 'var(--cell-width-row-number) + ' : ''}${Array(
809
- i
810
- )
811
- .fill(0)
812
- .map((_, idx) => `var(--cell-width-${idx})`)
813
- .join(' + ')})`
814
- : 'auto'};"
815
- on:click={(event) => handle_header_click(event, i)}
816
- on:mousedown={(event) => {
817
- event.preventDefault();
818
- event.stopPropagation();
819
- }}
820
- >
821
- <div class="cell-wrap">
822
- <div class="header-content">
823
- <EditableCell
824
- {max_chars}
825
- bind:value={_headers[i].value}
826
- bind:el={els[id].input}
827
- {latex_delimiters}
828
- {line_breaks}
829
- edit={header_edit === i}
830
- on:keydown={end_header_edit}
831
- header
832
- {root}
833
- {editable}
834
- />
835
- {#if header_edit !== i}
836
- <div class="sort-buttons">
837
- <SortIcon
838
- direction={sort_by === i ? sort_direction : null}
839
- on:sort={({ detail }) => handle_sort(i, detail)}
840
- {i18n}
841
- />
842
- </div>
843
- {/if}
844
- </div>
845
- {#if editable}
846
- <button
847
- class="cell-menu-button"
848
- on:click={(event) => toggle_header_menu(event, i)}
849
- on:touchstart={(event) => {
850
- event.preventDefault();
851
- const touch = event.touches[0];
852
- const mouseEvent = new MouseEvent("click", {
853
- clientX: touch.clientX,
854
- clientY: touch.clientY,
855
- bubbles: true,
856
- cancelable: true,
857
- view: window
858
- });
859
- toggle_header_menu(mouseEvent, i);
860
- }}
861
- >
862
- &#8942;
863
- </button>
864
- {/if}
865
- </div>
866
- </th>
664
+ <TableHeader
665
+ bind:value={_headers[i].value}
666
+ {i}
667
+ {actual_pinned_columns}
668
+ {header_edit}
669
+ {selected_header}
670
+ get_sort_status={df_actions.get_sort_status}
671
+ {headers}
672
+ {get_cell_width}
673
+ {handle_header_click}
674
+ {toggle_header_menu}
675
+ {end_header_edit}
676
+ sort_columns={$df_state.sort_state.sort_columns}
677
+ {latex_delimiters}
678
+ {line_breaks}
679
+ {max_chars}
680
+ {root}
681
+ {editable}
682
+ is_static={static_columns.includes(i)}
683
+ {i18n}
684
+ bind:el={els[id].input}
685
+ {col_count}
686
+ />
867
687
  {/each}
868
688
  </tr>
869
689
  </thead>
870
690
  <tbody>
871
691
  <tr>
692
+ {#if show_row_numbers}
693
+ <RowNumber index={0} />
694
+ {/if}
872
695
  {#each max as { value, id }, j (id)}
873
696
  <td tabindex="-1" bind:this={cells[j]}>
874
697
  <div class="cell-wrap">
@@ -881,6 +704,14 @@ function handle_header_click(event, col) {
881
704
  el={null}
882
705
  {root}
883
706
  {editable}
707
+ {i18n}
708
+ show_selection_buttons={selected_cells.length === 1 &&
709
+ selected_cells[0][0] === 0 &&
710
+ selected_cells[0][1] === j}
711
+ coords={[0, j]}
712
+ on_select_column={handle_select_column}
713
+ on_select_row={handle_select_row}
714
+ {is_dragging}
884
715
  />
885
716
  </div>
886
717
  </td>
@@ -903,7 +734,8 @@ function handle_header_click(event, col) {
903
734
  _headers = make_headers(
904
735
  head.map((h) => h ?? ""),
905
736
  col_count,
906
- els
737
+ els,
738
+ make_id
907
739
  );
908
740
  return _headers;
909
741
  },
@@ -916,258 +748,160 @@ function handle_header_click(event, col) {
916
748
  >
917
749
  <div class="table-wrap">
918
750
  <VirtualTable
919
- bind:items={data}
751
+ bind:items={search_results}
920
752
  {max_height}
921
753
  bind:actual_height={table_height}
922
754
  bind:table_scrollbar_width={scrollbar_width}
923
755
  selected={selected_index}
924
756
  disable_scroll={active_cell_menu !== null ||
925
757
  active_header_menu !== null}
758
+ bind:viewport
759
+ bind:show_scroll_button
760
+ on:scroll_top={(_) => {}}
926
761
  >
927
762
  {#if label && label.length !== 0}
928
763
  <caption class="sr-only">{label}</caption>
929
764
  {/if}
930
765
  <tr slot="thead">
931
766
  {#if show_row_numbers}
932
- <th
933
- class="row-number-header frozen-column always-frozen"
934
- style="left: 0;"
935
- >
936
- <div class="cell-wrap">
937
- <div class="header-content">
938
- <div class="header-text"></div>
939
- </div>
940
- </div>
941
- </th>
767
+ <RowNumber is_header={true} />
942
768
  {/if}
943
769
  {#each _headers as { value, id }, i (id)}
944
- <th
945
- class:frozen-column={i < actual_pinned_columns}
946
- class:last-frozen={i === actual_pinned_columns - 1}
947
- class:focus={header_edit === i || selected_header === i}
948
- aria-sort={get_sort_status(value, sort_by, sort_direction)}
949
- style="width: {get_cell_width(i)}; left: {i <
950
- actual_pinned_columns
951
- ? i === 0
952
- ? show_row_numbers
953
- ? 'var(--cell-width-row-number)'
954
- : '0'
955
- : `calc(${show_row_numbers ? 'var(--cell-width-row-number) + ' : ''}${Array(
956
- i
957
- )
958
- .fill(0)
959
- .map((_, idx) => `var(--cell-width-${idx})`)
960
- .join(' + ')})`
961
- : 'auto'};"
962
- on:click={(event) => handle_header_click(event, i)}
963
- on:mousedown={(event) => {
964
- event.preventDefault();
965
- event.stopPropagation();
966
- }}
967
- >
968
- <div class="cell-wrap">
969
- <div class="header-content">
970
- <EditableCell
971
- {max_chars}
972
- bind:value={_headers[i].value}
973
- bind:el={els[id].input}
974
- {latex_delimiters}
975
- {line_breaks}
976
- edit={header_edit === i}
977
- header
978
- {root}
979
- {editable}
980
- />
981
- {#if header_edit !== i}
982
- <div class="sort-buttons">
983
- <SortIcon
984
- direction={sort_by === i ? sort_direction : null}
985
- on:sort={({ detail }) => handle_sort(i, detail)}
986
- {i18n}
987
- />
988
- </div>
989
- {/if}
990
- </div>
991
- {#if editable}
992
- <button
993
- class="cell-menu-button"
994
- on:click={(event) => toggle_header_menu(event, i)}
995
- on:touchstart={(event) => {
996
- event.preventDefault();
997
- const touch = event.touches[0];
998
- const mouseEvent = new MouseEvent("click", {
999
- clientX: touch.clientX,
1000
- clientY: touch.clientY,
1001
- bubbles: true,
1002
- cancelable: true,
1003
- view: window
1004
- });
1005
- toggle_header_menu(mouseEvent, i);
1006
- }}
1007
- >
1008
- &#8942;
1009
- </button>
1010
- {/if}
1011
- </div>
1012
- </th>
770
+ <TableHeader
771
+ bind:value={_headers[i].value}
772
+ {i}
773
+ {actual_pinned_columns}
774
+ {header_edit}
775
+ {selected_header}
776
+ get_sort_status={df_actions.get_sort_status}
777
+ {headers}
778
+ {get_cell_width}
779
+ {handle_header_click}
780
+ {toggle_header_menu}
781
+ {end_header_edit}
782
+ sort_columns={$df_state.sort_state.sort_columns}
783
+ {latex_delimiters}
784
+ {line_breaks}
785
+ {max_chars}
786
+ {root}
787
+ {editable}
788
+ is_static={static_columns.includes(i)}
789
+ {i18n}
790
+ bind:el={els[id].input}
791
+ {col_count}
792
+ />
1013
793
  {/each}
1014
794
  </tr>
1015
- <tr slot="tbody" let:item let:index class:row_odd={index % 2 === 0}>
795
+ <tr slot="tbody" let:item let:index class:row-odd={index % 2 === 0}>
1016
796
  {#if show_row_numbers}
1017
- <td
1018
- class="row-number frozen-column always-frozen"
1019
- style="left: 0;"
1020
- tabindex="-1"
1021
- >
1022
- {index + 1}
1023
- </td>
797
+ <RowNumber {index} />
1024
798
  {/if}
1025
799
  {#each item as { value, id }, j (id)}
1026
- <td
1027
- class:frozen-column={j < actual_pinned_columns}
1028
- class:last-frozen={j === actual_pinned_columns - 1}
1029
- tabindex={show_row_numbers && j === 0 ? -1 : 0}
1030
- bind:this={els[id].cell}
1031
- on:touchstart={(event) => {
1032
- const touch = event.touches[0];
1033
- const mouseEvent = new MouseEvent("click", {
1034
- clientX: touch.clientX,
1035
- clientY: touch.clientY,
1036
- bubbles: true,
1037
- cancelable: true,
1038
- view: window
1039
- });
1040
- handle_cell_click(mouseEvent, index, j);
1041
- }}
1042
- on:mousedown={(event) => {
1043
- event.preventDefault();
1044
- event.stopPropagation();
1045
- }}
1046
- on:click={(event) => handle_cell_click(event, index, j)}
1047
- style="width: {get_cell_width(j)}; left: {j <
1048
- actual_pinned_columns
1049
- ? j === 0
1050
- ? show_row_numbers
1051
- ? 'var(--cell-width-row-number)'
1052
- : '0'
1053
- : `calc(${show_row_numbers ? 'var(--cell-width-row-number) + ' : ''}${Array(
1054
- j
1055
- )
1056
- .fill(0)
1057
- .map((_, idx) => `var(--cell-width-${idx})`)
1058
- .join(' + ')})`
1059
- : 'auto'}; {styling?.[index]?.[j] || ''}"
1060
- class:flash={copy_flash &&
1061
- is_cell_selected([index, j], selected_cells)}
1062
- class={is_cell_selected([index, j], selected_cells)}
1063
- class:menu-active={active_cell_menu &&
1064
- active_cell_menu.row === index &&
1065
- active_cell_menu.col === j}
1066
- >
1067
- <div class="cell-wrap">
1068
- <EditableCell
1069
- bind:value={data[index][j].value}
1070
- bind:el={els[id].input}
1071
- display_value={display_value?.[index]?.[j]}
1072
- {latex_delimiters}
1073
- {line_breaks}
1074
- {editable}
1075
- edit={dequal(editing, [index, j])}
1076
- datatype={Array.isArray(datatype) ? datatype[j] : datatype}
1077
- on:blur={() => {
1078
- clear_on_focus = false;
1079
- parent.focus();
1080
- }}
1081
- on:focus={() => {
1082
- const row = index;
1083
- const col = j;
1084
- if (
1085
- !selected_cells.some(([r, c]) => r === row && c === col)
1086
- ) {
1087
- selected_cells = [[row, col]];
1088
- }
1089
- }}
1090
- {clear_on_focus}
1091
- {root}
1092
- {max_chars}
1093
- />
1094
- {#if editable && should_show_cell_menu([index, j], selected_cells, editable)}
1095
- <button
1096
- class="cell-menu-button"
1097
- on:click={(event) => toggle_cell_menu(event, index, j)}
1098
- >
1099
- &#8942;
1100
- </button>
1101
- {/if}
1102
- </div>
1103
- </td>
800
+ <TableCell
801
+ bind:value={search_results[index][j].value}
802
+ {index}
803
+ {j}
804
+ {actual_pinned_columns}
805
+ {get_cell_width}
806
+ handle_cell_click={(e, r, c) => handle_mouse_down(e, r, c)}
807
+ {toggle_cell_menu}
808
+ {is_cell_selected}
809
+ {should_show_cell_menu}
810
+ {selected_cells}
811
+ {copy_flash}
812
+ {active_cell_menu}
813
+ styling={search_results[index][j].styling}
814
+ {latex_delimiters}
815
+ {line_breaks}
816
+ datatype={Array.isArray(datatype) ? datatype[j] : datatype}
817
+ {editing}
818
+ {clear_on_focus}
819
+ {max_chars}
820
+ {root}
821
+ {editable}
822
+ is_static={static_columns.includes(j)}
823
+ {i18n}
824
+ {components}
825
+ {handle_select_column}
826
+ {handle_select_row}
827
+ bind:el={els[id]}
828
+ {is_dragging}
829
+ />
1104
830
  {/each}
1105
831
  </tr>
1106
832
  </VirtualTable>
1107
833
  </div>
1108
834
  </Upload>
835
+ {#if show_scroll_button}
836
+ <button class="scroll-top-button" on:click={scroll_to_top}>
837
+ &uarr;
838
+ </button>
839
+ {/if}
1109
840
  </div>
1110
841
  </div>
1111
842
  {#if data.length === 0 && editable && row_count[1] === "dynamic"}
1112
- <div class="add-row-container">
1113
- <button class="add-row-button" on:click={() => add_row()}>
1114
- <span>+</span>
1115
- </button>
1116
- </div>
1117
- {/if}
1118
-
1119
- {#if active_cell_menu}
1120
- <CellMenu
1121
- x={active_cell_menu.x}
1122
- y={active_cell_menu.y}
1123
- row={active_cell_menu.row}
1124
- {col_count}
1125
- {row_count}
1126
- on_add_row_above={() => add_row_at(active_cell_menu?.row || 0, "above")}
1127
- on_add_row_below={() => add_row_at(active_cell_menu?.row || 0, "below")}
1128
- on_add_column_left={() => add_col_at(active_cell_menu?.col || 0, "left")}
1129
- on_add_column_right={() => add_col_at(active_cell_menu?.col || 0, "right")}
1130
- on_delete_row={() => delete_row_at(active_cell_menu?.row || 0)}
1131
- on_delete_col={() => delete_col_at(active_cell_menu?.col || 0)}
1132
- can_delete_rows={data.length > 1}
1133
- can_delete_cols={data[0].length > 1}
1134
- {i18n}
1135
- />
843
+ <EmptyRowButton on_click={() => add_row()} />
1136
844
  {/if}
1137
845
 
1138
- {#if active_header_menu !== null}
846
+ {#if active_cell_menu || active_header_menu}
1139
847
  <CellMenu
1140
- {i18n}
1141
- x={active_header_menu.x}
1142
- y={active_header_menu.y}
1143
- row={-1}
848
+ x={active_cell_menu?.x ?? active_header_menu?.x ?? 0}
849
+ y={active_cell_menu?.y ?? active_header_menu?.y ?? 0}
850
+ row={active_header_menu ? -1 : active_cell_menu?.row ?? 0}
1144
851
  {col_count}
1145
852
  {row_count}
1146
853
  on_add_row_above={() => add_row_at(active_cell_menu?.row ?? -1, "above")}
1147
854
  on_add_row_below={() => add_row_at(active_cell_menu?.row ?? -1, "below")}
1148
- on_add_column_left={() => add_col_at(active_header_menu?.col ?? -1, "left")}
855
+ on_add_column_left={() =>
856
+ add_col_at(
857
+ active_cell_menu?.col ?? active_header_menu?.col ?? -1,
858
+ "left"
859
+ )}
1149
860
  on_add_column_right={() =>
1150
- add_col_at(active_header_menu?.col ?? -1, "right")}
861
+ add_col_at(
862
+ active_cell_menu?.col ?? active_header_menu?.col ?? -1,
863
+ "right"
864
+ )}
1151
865
  on_delete_row={() => delete_row_at(active_cell_menu?.row ?? -1)}
1152
- on_delete_col={() => delete_col_at(active_header_menu?.col ?? -1)}
1153
- can_delete_rows={false}
1154
- can_delete_cols={_headers.length > 1}
866
+ on_delete_col={() =>
867
+ delete_col_at(active_cell_menu?.col ?? active_header_menu?.col ?? -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}
871
+ {i18n}
872
+ on_sort={active_header_menu
873
+ ? (direction) => {
874
+ if (active_header_menu) {
875
+ handle_sort(active_header_menu.col, direction);
876
+ df_actions.set_active_header_menu(null);
877
+ }
878
+ }
879
+ : undefined}
880
+ on_clear_sort={active_header_menu
881
+ ? () => {
882
+ clear_sort();
883
+ df_actions.set_active_header_menu(null);
884
+ }
885
+ : undefined}
886
+ sort_direction={active_header_menu
887
+ ? $df_state.sort_state.sort_columns.find(
888
+ (item) => item.col === (active_header_menu?.col ?? -1)
889
+ )?.direction ?? null
890
+ : null}
891
+ sort_priority={active_header_menu
892
+ ? $df_state.sort_state.sort_columns.findIndex(
893
+ (item) => item.col === (active_header_menu?.col ?? -1)
894
+ ) + 1 || null
895
+ : null}
1155
896
  />
1156
897
  {/if}
1157
898
 
1158
899
  <style>
1159
- .label p {
1160
- position: relative;
1161
- z-index: var(--layer-4);
1162
- margin-bottom: var(--size-2);
1163
- color: var(--block-label-text-color);
1164
- font-size: var(--block-label-text-size);
1165
- }
1166
-
1167
900
  .table-container {
1168
901
  display: flex;
1169
902
  flex-direction: column;
1170
903
  gap: var(--size-2);
904
+ position: relative;
1171
905
  }
1172
906
 
1173
907
  .table-wrap {
@@ -1183,17 +917,26 @@ function handle_header_click(event, col) {
1183
917
  outline: none;
1184
918
  }
1185
919
 
1186
- .dragging {
1187
- border-color: var(--color-accent);
920
+ .table-wrap.dragging {
921
+ cursor: crosshair !important;
922
+ user-select: none;
1188
923
  }
1189
924
 
1190
- .no-wrap {
1191
- white-space: nowrap;
925
+ .table-wrap.dragging * {
926
+ cursor: crosshair !important;
927
+ user-select: none;
928
+ }
929
+
930
+ .table-wrap > :global(button) {
931
+ border: 1px solid var(--border-color-primary);
932
+ border-radius: var(--table-radius);
933
+ overflow: hidden;
1192
934
  }
1193
935
 
1194
936
  table {
1195
937
  position: absolute;
1196
938
  opacity: 0;
939
+ z-index: -1;
1197
940
  transition: 150ms;
1198
941
  width: var(--size-full);
1199
942
  table-layout: auto;
@@ -1205,159 +948,39 @@ function handle_header_click(event, col) {
1205
948
  border-collapse: separate;
1206
949
  }
1207
950
 
1208
- .table-wrap > :global(button) {
1209
- border: 1px solid var(--border-color-primary);
1210
- border-radius: var(--table-radius);
1211
- overflow: hidden;
1212
- }
1213
-
1214
- div:not(.no-wrap) td {
1215
- overflow-wrap: anywhere;
1216
- }
1217
-
1218
- div.no-wrap td {
1219
- overflow-x: hidden;
1220
- }
1221
-
1222
- table.fixed-layout {
1223
- table-layout: fixed;
1224
- }
1225
-
1226
951
  thead {
1227
952
  position: sticky;
1228
953
  top: 0;
1229
- z-index: var(--layer-2);
954
+ z-index: 5;
1230
955
  box-shadow: var(--shadow-drop);
1231
956
  }
1232
957
 
1233
- tr {
1234
- border-bottom: 1px solid var(--border-color-primary);
1235
- text-align: left;
1236
- }
1237
-
1238
- tr > * + * {
1239
- border-right-width: 0px;
1240
- border-left-width: 1px;
1241
- border-style: solid;
1242
- border-color: var(--border-color-primary);
1243
- }
1244
-
1245
- th,
1246
- td {
1247
- --ring-color: transparent;
1248
- position: relative;
1249
- outline: none;
1250
- box-shadow: inset 0 0 0 1px var(--ring-color);
1251
- padding: 0;
1252
- }
1253
-
1254
- th:first-child {
1255
- border-top-left-radius: var(--table-radius);
1256
- border-bottom-left-radius: var(--table-radius);
1257
- }
1258
-
1259
- th:last-child {
1260
- border-top-right-radius: var(--table-radius);
1261
- border-bottom-right-radius: var(--table-radius);
1262
- }
1263
-
1264
- th.focus,
1265
- td.focus {
1266
- --ring-color: var(--color-accent);
1267
- box-shadow: inset 0 0 0 2px var(--ring-color);
1268
- z-index: var(--layer-1);
1269
- }
1270
-
1271
- th.focus {
1272
- z-index: var(--layer-2);
1273
- }
1274
-
1275
- tr:last-child td:first-child {
1276
- border-bottom-left-radius: var(--table-radius);
1277
- }
1278
-
1279
- tr:last-child td:last-child {
1280
- border-bottom-right-radius: var(--table-radius);
1281
- }
1282
-
1283
- tr th {
1284
- background: var(--table-even-background-fill);
958
+ thead :global(th.pinned-column) {
959
+ position: sticky;
960
+ z-index: 6;
961
+ background: var(--table-even-background-fill) !important;
1285
962
  }
1286
963
 
1287
- .sort-buttons {
1288
- display: flex;
1289
- align-items: center;
1290
- flex-shrink: 0;
1291
- order: -1;
964
+ .dragging {
965
+ border-color: var(--color-accent);
1292
966
  }
1293
967
 
1294
- .editing {
1295
- background: var(--table-editing);
968
+ .no-wrap {
969
+ white-space: nowrap;
1296
970
  }
1297
971
 
1298
- .cell-wrap {
1299
- display: flex;
1300
- align-items: center;
1301
- justify-content: flex-start;
1302
- outline: none;
1303
- min-height: var(--size-9);
1304
- position: relative;
1305
- height: 100%;
1306
- padding: var(--size-2);
1307
- box-sizing: border-box;
1308
- margin: 0;
1309
- gap: var(--size-1);
1310
- overflow: visible;
1311
- min-width: 0;
1312
- border-radius: var(--table-radius);
972
+ div:not(.no-wrap) td {
973
+ overflow-wrap: anywhere;
1313
974
  }
1314
975
 
1315
- .header-content {
1316
- display: flex;
1317
- align-items: center;
1318
- overflow: hidden;
1319
- flex-grow: 1;
1320
- min-width: 0;
1321
- white-space: normal;
1322
- overflow-wrap: break-word;
1323
- word-break: normal;
1324
- height: 100%;
1325
- gap: var(--size-1);
976
+ div.no-wrap td {
977
+ overflow-x: hidden;
1326
978
  }
1327
979
 
1328
- .row_odd {
980
+ .row-odd {
1329
981
  background: var(--table-odd-background-fill);
1330
982
  }
1331
983
 
1332
- .row_odd.focus {
1333
- background: var(--background-fill-primary);
1334
- }
1335
-
1336
- .cell-menu-button {
1337
- flex-shrink: 0;
1338
- display: none;
1339
- background-color: var(--block-background-fill);
1340
- border: 1px solid var(--border-color-primary);
1341
- border-radius: var(--block-radius);
1342
- width: var(--size-5);
1343
- height: var(--size-5);
1344
- min-width: var(--size-5);
1345
- padding: 0;
1346
- margin-right: var(--spacing-sm);
1347
- z-index: var(--layer-1);
1348
- position: absolute;
1349
- right: var(--size-1);
1350
- top: 50%;
1351
- transform: translateY(-50%);
1352
- }
1353
-
1354
- .cell-selected .cell-menu-button,
1355
- th:hover .cell-menu-button {
1356
- display: flex;
1357
- align-items: center;
1358
- justify-content: center;
1359
- }
1360
-
1361
984
  .header-row {
1362
985
  display: flex;
1363
986
  justify-content: flex-end;
@@ -1368,225 +991,45 @@ function handle_header_click(event, col) {
1368
991
  width: 100%;
1369
992
  }
1370
993
 
1371
- .label {
994
+ .header-row .label {
1372
995
  flex: 1 1 auto;
1373
996
  margin-right: auto;
1374
997
  }
1375
998
 
1376
- .label p {
999
+ .header-row .label p {
1377
1000
  margin: 0;
1378
1001
  color: var(--block-label-text-color);
1379
1002
  font-size: var(--block-label-text-size);
1380
1003
  line-height: var(--line-sm);
1381
- }
1382
-
1383
- .toolbar {
1384
- flex: 0 0 auto;
1385
- }
1386
-
1387
- .row-number,
1388
- .row-number-header {
1389
- text-align: center;
1390
- background: var(--table-even-background-fill);
1391
- font-size: var(--input-text-size);
1392
- color: var(--body-text-color);
1393
- padding: var(--size-1);
1394
- min-width: var(--size-12);
1395
- width: var(--size-12);
1396
- overflow: hidden;
1397
- text-overflow: ellipsis;
1398
- white-space: nowrap;
1399
- font-weight: var(--weight-semibold);
1400
- }
1401
-
1402
- .row-number-header .header-content {
1403
- justify-content: space-between;
1404
- padding: var(--size-1);
1405
- height: var(--size-9);
1406
- display: flex;
1407
- align-items: center;
1408
- }
1409
-
1410
- .row-number-header :global(.sort-icons) {
1411
- margin-right: 0;
1412
- }
1413
-
1414
- :global(tbody > tr:nth-child(odd)) .row-number {
1415
- background: var(--table-odd-background-fill);
1416
- }
1417
-
1418
- .cell-selected {
1419
- --ring-color: var(--color-accent);
1420
- box-shadow: inset 0 0 0 2px var(--ring-color);
1421
- z-index: var(--layer-1);
1422
1004
  position: relative;
1005
+ z-index: 4;
1423
1006
  }
1424
1007
 
1425
- .cell-selected.no-top {
1426
- box-shadow:
1427
- inset 2px 0 0 var(--ring-color),
1428
- inset -2px 0 0 var(--ring-color),
1429
- inset 0 -2px 0 var(--ring-color);
1430
- }
1431
-
1432
- .cell-selected.no-bottom {
1433
- box-shadow:
1434
- inset 2px 0 0 var(--ring-color),
1435
- inset -2px 0 0 var(--ring-color),
1436
- inset 0 2px 0 var(--ring-color);
1437
- }
1438
-
1439
- .cell-selected.no-left {
1440
- box-shadow:
1441
- inset 0 2px 0 var(--ring-color),
1442
- inset -2px 0 0 var(--ring-color),
1443
- inset 0 -2px 0 var(--ring-color);
1444
- }
1445
-
1446
- .cell-selected.no-right {
1447
- box-shadow:
1448
- inset 0 2px 0 var(--ring-color),
1449
- inset 2px 0 0 var(--ring-color),
1450
- inset 0 -2px 0 var(--ring-color);
1451
- }
1452
-
1453
- .cell-selected.no-top.no-left {
1454
- box-shadow:
1455
- inset -2px 0 0 var(--ring-color),
1456
- inset 0 -2px 0 var(--ring-color);
1457
- }
1458
-
1459
- .cell-selected.no-top.no-right {
1460
- box-shadow:
1461
- inset 2px 0 0 var(--ring-color),
1462
- inset 0 -2px 0 var(--ring-color);
1463
- }
1464
-
1465
- .cell-selected.no-bottom.no-left {
1466
- box-shadow:
1467
- inset -2px 0 0 var(--ring-color),
1468
- inset 0 2px 0 var(--ring-color);
1469
- }
1470
-
1471
- .cell-selected.no-bottom.no-right {
1472
- box-shadow:
1473
- inset 2px 0 0 var(--ring-color),
1474
- inset 0 2px 0 var(--ring-color);
1475
- }
1476
-
1477
- .cell-selected.no-top.no-bottom {
1478
- box-shadow:
1479
- inset 2px 0 0 var(--ring-color),
1480
- inset -2px 0 0 var(--ring-color);
1481
- }
1482
-
1483
- .cell-selected.no-left.no-right {
1484
- box-shadow:
1485
- inset 0 2px 0 var(--ring-color),
1486
- inset 0 -2px 0 var(--ring-color);
1487
- }
1488
-
1489
- .cell-selected.no-top.no-left.no-right {
1490
- box-shadow: inset 0 -2px 0 var(--ring-color);
1491
- }
1492
-
1493
- .cell-selected.no-bottom.no-left.no-right {
1494
- box-shadow: inset 0 2px 0 var(--ring-color);
1495
- }
1496
-
1497
- .cell-selected.no-left.no-top.no-bottom {
1498
- box-shadow: inset -2px 0 0 var(--ring-color);
1499
- }
1500
-
1501
- .cell-selected.no-right.no-top.no-bottom {
1502
- box-shadow: inset 2px 0 0 var(--ring-color);
1503
- }
1504
-
1505
- .cell-selected.no-top.no-bottom.no-left.no-right {
1506
- box-shadow: none;
1507
- }
1508
-
1509
- .selection-button {
1008
+ .scroll-top-button {
1510
1009
  position: absolute;
1010
+ right: var(--size-4);
1011
+ bottom: var(--size-4);
1012
+ width: var(--size-8);
1013
+ height: var(--size-8);
1014
+ border-radius: var(--table-radius);
1015
+ background: var(--color-accent);
1016
+ color: white;
1017
+ border: none;
1018
+ cursor: pointer;
1511
1019
  display: flex;
1512
1020
  align-items: center;
1513
1021
  justify-content: center;
1514
- background: var(--color-accent);
1515
- color: white;
1516
- border-radius: var(--radius-sm);
1517
- z-index: var(--layer-4);
1518
- }
1519
-
1520
- .selection-button-column {
1521
- width: var(--size-3);
1522
- height: var(--size-5);
1523
- top: -10px;
1524
- left: var(--selected-col-pos);
1525
- transform: rotate(90deg);
1022
+ font-size: var(--text-lg);
1023
+ z-index: 9;
1024
+ opacity: 0.5;
1526
1025
  }
1527
1026
 
1528
- .selection-button-row {
1529
- width: var(--size-3);
1530
- height: var(--size-5);
1531
- left: -7px;
1532
- top: calc(var(--selected-row-pos) - var(--size-5) / 2);
1027
+ .scroll-top-button:hover {
1028
+ opacity: 1;
1533
1029
  }
1534
1030
 
1535
- .table-wrap:not(:focus-within) .selection-button {
1536
- opacity: 0;
1537
- pointer-events: none;
1538
- }
1539
-
1540
- .flash.cell-selected {
1541
- animation: flash-color 700ms ease-out;
1542
- }
1543
-
1544
- @keyframes flash-color {
1545
- 0%,
1546
- 30% {
1547
- background: var(--color-accent-copied);
1548
- }
1549
-
1550
- 100% {
1551
- background: transparent;
1552
- }
1553
- }
1554
-
1555
- .frozen-column {
1556
- position: sticky;
1557
- z-index: var(--layer-2);
1558
- border-right: 1px solid var(--border-color-primary);
1559
- }
1560
-
1561
- tr:nth-child(odd) .frozen-column {
1562
- background: var(--table-odd-background-fill);
1563
- }
1564
-
1565
- tr:nth-child(even) .frozen-column {
1566
- background: var(--table-even-background-fill);
1567
- }
1568
-
1569
- .always-frozen {
1570
- z-index: var(--layer-3);
1571
- }
1572
-
1573
- .add-row-container {
1574
- margin-top: var(--size-2);
1575
- }
1576
-
1577
- .add-row-button {
1578
- width: 100%;
1579
- padding: var(--size-1);
1580
- background: transparent;
1581
- border: 1px dashed var(--border-color-primary);
1582
- border-radius: var(--radius-sm);
1583
- color: var(--body-text-color);
1584
- cursor: pointer;
1585
- transition: all 150ms;
1586
- }
1587
-
1588
- .add-row-button:hover {
1589
- background: var(--background-fill-secondary);
1590
- border-style: solid;
1031
+ tr {
1032
+ border-bottom: 1px solid var(--border-color-primary);
1033
+ text-align: left;
1591
1034
  }
1592
1035
  </style>