@gradio/dataframe 0.20.1 → 0.21.0-dev.3

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 (58) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/Dataframe.stories.svelte +0 -25
  3. package/Example.svelte +17 -13
  4. package/Index.svelte +114 -110
  5. package/dist/Example.svelte +26 -20
  6. package/dist/Example.svelte.d.ts +21 -19
  7. package/dist/Index.svelte +121 -99
  8. package/dist/Index.svelte.d.ts +4 -168
  9. package/dist/shared/BooleanCell.svelte +12 -9
  10. package/dist/shared/BooleanCell.svelte.d.ts +20 -18
  11. package/dist/shared/CellMenu.svelte +82 -64
  12. package/dist/shared/CellMenu.svelte.d.ts +39 -37
  13. package/dist/shared/CellMenuButton.svelte +2 -1
  14. package/dist/shared/CellMenuButton.svelte.d.ts +18 -16
  15. package/dist/shared/CellMenuIcons.svelte +2 -1
  16. package/dist/shared/CellMenuIcons.svelte.d.ts +18 -16
  17. package/dist/shared/EditableCell.svelte +97 -56
  18. package/dist/shared/EditableCell.svelte.d.ts +50 -48
  19. package/dist/shared/EmptyRowButton.svelte +2 -1
  20. package/dist/shared/EmptyRowButton.svelte.d.ts +18 -16
  21. package/dist/shared/Example.svelte +2 -1
  22. package/dist/shared/Example.svelte.d.ts +18 -16
  23. package/dist/shared/FilterMenu.svelte +53 -39
  24. package/dist/shared/FilterMenu.svelte.d.ts +20 -18
  25. package/dist/shared/RowNumber.svelte +3 -2
  26. package/dist/shared/RowNumber.svelte.d.ts +19 -17
  27. package/dist/shared/Table.svelte +826 -624
  28. package/dist/shared/Table.svelte.d.ts +59 -57
  29. package/dist/shared/TableCell.svelte +95 -50
  30. package/dist/shared/TableCell.svelte.d.ts +61 -59
  31. package/dist/shared/TableHeader.svelte +86 -58
  32. package/dist/shared/TableHeader.svelte.d.ts +55 -53
  33. package/dist/shared/Toolbar.svelte +49 -39
  34. package/dist/shared/Toolbar.svelte.d.ts +27 -25
  35. package/dist/shared/VirtualTable.svelte +207 -154
  36. package/dist/shared/VirtualTable.svelte.d.ts +40 -37
  37. package/dist/shared/icons/FilterIcon.svelte +2 -1
  38. package/dist/shared/icons/FilterIcon.svelte.d.ts +16 -14
  39. package/dist/shared/icons/Padlock.svelte.d.ts +22 -21
  40. package/dist/shared/icons/SelectionButtons.svelte +15 -5
  41. package/dist/shared/icons/SelectionButtons.svelte.d.ts +20 -18
  42. package/dist/shared/icons/SortArrowDown.svelte +2 -1
  43. package/dist/shared/icons/SortArrowDown.svelte.d.ts +18 -16
  44. package/dist/shared/icons/SortArrowUp.svelte +2 -1
  45. package/dist/shared/icons/SortArrowUp.svelte.d.ts +18 -16
  46. package/dist/shared/icons/SortButtonDown.svelte.d.ts +22 -21
  47. package/dist/shared/icons/SortButtonUp.svelte.d.ts +22 -21
  48. package/dist/shared/icons/SortIcon.svelte +12 -8
  49. package/dist/shared/icons/SortIcon.svelte.d.ts +22 -20
  50. package/dist/shared/utils/data_processing.d.ts +1 -1
  51. package/dist/standalone/Index.svelte +104 -78
  52. package/dist/standalone/Index.svelte.d.ts +41 -41
  53. package/dist/standalone/stubs/Upload.svelte +5 -4
  54. package/dist/standalone/stubs/Upload.svelte.d.ts +35 -25
  55. package/package.json +15 -14
  56. package/shared/Table.svelte +13 -11
  57. package/shared/VirtualTable.svelte +1 -1
  58. package/standalone/Index.svelte +17 -19
@@ -1,629 +1,829 @@
1
- <script context="module">import {
2
- create_dataframe_context
3
- } from "./context/dataframe_context";
1
+ <script lang="ts" context="module">
2
+ import {
3
+ create_dataframe_context,
4
+ type SortDirection,
5
+ type FilterDatatype
6
+ } from "./context/dataframe_context";
4
7
  </script>
5
8
 
6
- <script>import { afterUpdate, createEventDispatcher, tick, onMount } from "svelte";
7
- import { dequal } from "dequal/lite";
8
- import { Upload } from "@gradio/upload";
9
- import EditableCell from "./EditableCell.svelte";
10
- import RowNumber from "./RowNumber.svelte";
11
- import TableHeader from "./TableHeader.svelte";
12
- import TableCell from "./TableCell.svelte";
13
- import EmptyRowButton from "./EmptyRowButton.svelte";
14
- import {} from "@gradio/client";
15
- import VirtualTable from "./VirtualTable.svelte";
16
- import CellMenu from "./CellMenu.svelte";
17
- import Toolbar from "./Toolbar.svelte";
18
- import {
19
- is_cell_selected,
20
- should_show_cell_menu,
21
- get_current_indices,
22
- handle_click_outside as handle_click_outside_util,
23
- calculate_selection_positions
24
- } from "./utils/selection_utils";
25
- import {
26
- copy_table_data,
27
- get_max,
28
- handle_file_upload
29
- } from "./utils/table_utils";
30
- import { make_headers, process_data } from "./utils/data_processing";
31
- import { handle_keydown, handle_cell_blur } from "./utils/keyboard_utils";
32
- import {
33
- create_drag_handlers
34
- } from "./utils/drag_utils";
35
- import { sort_data_and_preserve_selection } from "./utils/sort_utils";
36
- import { filter_data_and_preserve_selection } from "./utils/filter_utils";
37
- export let datatype;
38
- export let label = null;
39
- export let show_label = true;
40
- export let headers = [];
41
- export let values = [];
42
- export let col_count;
43
- export let row_count;
44
- export let latex_delimiters;
45
- export let components = {};
46
- export let editable = true;
47
- export let wrap = false;
48
- export let root;
49
- export let i18n;
50
- export let max_height = 500;
51
- export let line_breaks = true;
52
- export let column_widths = [];
53
- export let show_row_numbers = false;
54
- export let upload;
55
- export let stream_handler;
56
- export let show_fullscreen_button = false;
57
- export let show_copy_button = false;
58
- export let value_is_output = false;
59
- export let max_chars = void 0;
60
- export let show_search = "none";
61
- export let pinned_columns = 0;
62
- export let static_columns = [];
63
- export let fullscreen = false;
64
- const df_ctx = create_dataframe_context({
65
- show_fullscreen_button,
66
- show_copy_button,
67
- show_search,
68
- show_row_numbers,
69
- editable,
70
- pinned_columns,
71
- show_label,
72
- line_breaks,
73
- wrap,
74
- max_height,
75
- column_widths,
76
- max_chars,
77
- static_columns
78
- });
79
- const { state: df_state, actions: df_actions } = df_ctx;
80
- $: selected_cells = $df_state.ui_state.selected_cells;
81
- $: selected = $df_state.ui_state.selected;
82
- $: editing = $df_state.ui_state.editing;
83
- $: header_edit = $df_state.ui_state.header_edit;
84
- $: selected_header = $df_state.ui_state.selected_header;
85
- $: active_cell_menu = $df_state.ui_state.active_cell_menu;
86
- $: active_header_menu = $df_state.ui_state.active_header_menu;
87
- $: copy_flash = $df_state.ui_state.copy_flash;
88
- $: actual_pinned_columns = pinned_columns && data?.[0]?.length ? Math.min(pinned_columns, data[0].length) : 0;
89
- onMount(() => {
90
- df_ctx.parent_element = parent;
91
- df_ctx.get_data_at = get_data_at;
92
- df_ctx.get_column = get_column;
93
- df_ctx.get_row = get_row;
94
- df_ctx.dispatch = dispatch;
95
- init_drag_handlers();
96
- const observer = new IntersectionObserver((entries) => {
97
- entries.forEach((entry) => {
98
- if (entry.isIntersecting && !is_visible) {
99
- width_calculated = false;
100
- }
101
- is_visible = entry.isIntersecting;
102
- });
103
- });
104
- observer.observe(parent);
105
- document.addEventListener("click", handle_click_outside);
106
- window.addEventListener("resize", handle_resize);
107
- const global_mouse_up = (event) => {
108
- if (is_dragging || drag_start) {
109
- handle_mouse_up(event);
110
- }
111
- };
112
- document.addEventListener("mouseup", global_mouse_up);
113
- return () => {
114
- observer.disconnect();
115
- document.removeEventListener("click", handle_click_outside);
116
- window.removeEventListener("resize", handle_resize);
117
- document.removeEventListener("mouseup", global_mouse_up);
118
- };
119
- });
120
- $: {
121
- if (data || _headers || els) {
122
- df_ctx.data = data;
123
- df_ctx.headers = _headers;
124
- df_ctx.els = els;
125
- df_ctx.display_value = display_value;
126
- df_ctx.styling = styling;
127
- }
128
- }
129
- const dispatch = createEventDispatcher();
130
- let els = {};
131
- let data_binding = {};
132
- let _headers = make_headers(headers, col_count, els, make_id);
133
- let old_headers = headers;
134
- let data = [[]];
135
- let old_val = void 0;
136
- let search_results = [[]];
137
- let dragging = false;
138
- let color_accent_copied;
139
- let filtered_to_original_map = [];
140
- onMount(() => {
141
- const color = getComputedStyle(document.documentElement).getPropertyValue("--color-accent").trim();
142
- color_accent_copied = color + "40";
143
- document.documentElement.style.setProperty(
144
- "--color-accent-copied",
145
- color_accent_copied
146
- );
147
- });
148
- const get_data_at = (row, col) => data?.[row]?.[col]?.value;
149
- const get_column = (col) => data?.map((row) => row[col]?.value) ?? [];
150
- const get_row = (row) => data?.[row]?.map((cell) => cell.value) ?? [];
151
- $: {
152
- if (!dequal(headers, old_headers)) {
153
- _headers = make_headers(headers, col_count, els, make_id);
154
- old_headers = JSON.parse(JSON.stringify(headers));
155
- }
156
- }
157
- function make_id() {
158
- return Math.random().toString(36).substring(2, 15);
159
- }
160
- export let display_value = null;
161
- export let styling = null;
162
- $: if (!dequal(values, old_val)) {
163
- if (parent) {
164
- const is_reset2 = values.length === 0 || values.length === 1 && values[0].length === 0;
165
- 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);
166
- if (is_reset2 || is_different_structure2) {
167
- for (let i = 0; i < 50; i++) {
168
- parent.style.removeProperty(`--cell-width-${i}`);
169
- }
170
- last_width_data_length = 0;
171
- last_width_column_count = 0;
172
- width_calculated = false;
173
- }
174
- }
175
- const is_reset = values.length === 0 || values.length === 1 && values[0].length === 0;
176
- 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);
177
- data = process_data(
178
- values,
179
- els,
180
- data_binding,
181
- make_id,
182
- display_value,
183
- datatype
184
- );
185
- old_val = JSON.parse(JSON.stringify(values));
186
- if (is_reset || is_different_structure) {
187
- df_actions.reset_sort_state();
188
- } else if ($df_state.sort_state.sort_columns.length > 0) {
189
- sort_data(data, display_value, styling);
190
- } else {
191
- df_actions.handle_sort(-1, "asc");
192
- df_actions.reset_sort_state();
193
- }
194
- if ($df_state.filter_state.filter_columns.length > 0) {
195
- filter_data(data, display_value, styling);
196
- } else {
197
- df_actions.reset_filter_state();
198
- }
199
- if ($df_state.current_search_query) {
200
- df_actions.handle_search(null);
201
- }
202
- if (parent && cells.length > 0 && (is_reset || is_different_structure)) {
203
- width_calculated = false;
204
- }
205
- }
206
- $: if ($df_state.current_search_query !== void 0) {
207
- const cell_map = /* @__PURE__ */ new Map();
208
- filtered_to_original_map = [];
209
- data.forEach((row, row_idx) => {
210
- if (row.some(
211
- (cell) => String(cell?.value).toLowerCase().includes($df_state.current_search_query?.toLowerCase() || "")
212
- )) {
213
- filtered_to_original_map.push(row_idx);
214
- }
215
- row.forEach((cell, col_idx) => {
216
- cell_map.set(cell.id, {
217
- value: cell.value,
218
- display_value: cell.display_value !== void 0 ? cell.display_value : String(cell.value),
219
- styling: styling?.[row_idx]?.[col_idx] || ""
220
- });
221
- });
222
- });
223
- const filtered = df_actions.filter_data(data);
224
- search_results = filtered.map(
225
- (row) => row.map((cell) => {
226
- const original = cell_map.get(cell.id);
227
- return {
228
- ...cell,
229
- display_value: original?.display_value !== void 0 ? original.display_value : String(cell.value),
230
- styling: original?.styling || ""
231
- };
232
- })
233
- );
234
- } else {
235
- filtered_to_original_map = [];
236
- }
237
- let previous_headers = _headers.map((h) => h.value);
238
- let previous_data = data.map((row) => row.map((cell) => cell.value));
239
- $: {
240
- if (data || _headers) {
241
- df_actions.trigger_change(
242
- data,
243
- _headers,
244
- previous_data,
245
- previous_headers,
246
- value_is_output,
247
- dispatch
248
- );
249
- previous_data = data.map((row) => row.map((cell) => cell.value));
250
- previous_headers = _headers.map((h) => h.value);
251
- }
252
- }
253
- function handle_sort(col, direction) {
254
- df_actions.handle_sort(col, direction);
255
- sort_data(data, display_value, styling);
256
- }
257
- function clear_sort() {
258
- df_actions.reset_sort_state();
259
- sort_data(data, display_value, styling);
260
- }
261
- $: {
262
- if ($df_state.filter_state.filter_columns.length > 0) {
263
- filter_data(data, display_value, styling);
264
- }
265
- if ($df_state.sort_state.sort_columns.length > 0) {
266
- sort_data(data, display_value, styling);
267
- df_actions.update_row_order(data);
268
- }
269
- }
270
- function handle_filter(col, datatype2, filter, value) {
271
- df_actions.handle_filter(col, datatype2, filter, value);
272
- filter_data(data, display_value, styling);
273
- }
274
- function clear_filter() {
275
- df_actions.reset_filter_state();
276
- filter_data(data, display_value, styling);
277
- }
278
- async function edit_header(i, _select = false) {
279
- if (!editable || header_edit === i || col_count[1] !== "dynamic") return;
280
- df_actions.set_header_edit(i);
281
- }
282
- function handle_header_click(event, col) {
283
- if (event.target instanceof HTMLAnchorElement) {
284
- return;
285
- }
286
- event.preventDefault();
287
- event.stopPropagation();
288
- if (!editable) return;
289
- df_actions.set_editing(false);
290
- df_actions.handle_header_click(col, editable);
291
- parent.focus();
292
- }
293
- function end_header_edit(event) {
294
- if (!editable) return;
295
- df_actions.end_header_edit(event.detail.key);
296
- parent.focus();
297
- }
298
- async function add_row(index) {
299
- parent.focus();
300
- if (row_count[1] !== "dynamic") return;
301
- const new_row = Array(data[0]?.length || headers.length).fill(0).map((_, i) => {
302
- const _id = make_id();
303
- els[_id] = { cell: null, input: null };
304
- return { id: _id, value: "" };
305
- });
306
- if (data.length === 0) {
307
- data = [new_row];
308
- } else if (index !== void 0 && index >= 0 && index <= data.length) {
309
- data.splice(index, 0, new_row);
310
- } else {
311
- data.push(new_row);
312
- }
313
- selected = [index !== void 0 ? index : data.length - 1, 0];
314
- }
315
- async function add_col(index) {
316
- parent.focus();
317
- if (col_count[1] !== "dynamic") return;
318
- const result = df_actions.add_col(data, headers, make_id, index);
319
- result.data.forEach((row) => {
320
- row.forEach((cell) => {
321
- if (!els[cell.id]) {
322
- els[cell.id] = { cell: null, input: null };
323
- }
324
- });
325
- });
326
- data = result.data;
327
- headers = result.headers;
328
- await tick();
329
- requestAnimationFrame(() => {
330
- edit_header(index !== void 0 ? index : data[0].length - 1, true);
331
- const new_w = parent.querySelectorAll("tbody")[1].offsetWidth;
332
- parent.querySelectorAll("table")[1].scrollTo({ left: new_w });
333
- });
334
- }
335
- function handle_click_outside(event) {
336
- if (handle_click_outside_util(event, parent)) {
337
- df_actions.clear_ui_state();
338
- header_edit = false;
339
- selected_header = false;
340
- }
341
- }
342
- $: max = get_max(data);
343
- let width_calc_timeout;
344
- $: if (cells[0] && cells[0]?.clientWidth) {
345
- clearTimeout(width_calc_timeout);
346
- width_calc_timeout = setTimeout(() => set_cell_widths(), 100);
347
- }
348
- let width_calculated = false;
349
- $: if (cells[0] && !width_calculated) {
350
- set_cell_widths();
351
- width_calculated = true;
352
- }
353
- let cells = [];
354
- let parent;
355
- let table;
356
- let last_width_data_length = 0;
357
- let last_width_column_count = 0;
358
- function set_cell_widths() {
359
- const column_count = data[0]?.length || 0;
360
- if ($df_state.filter_state.filter_columns.length > 0) {
361
- return;
362
- }
363
- if (last_width_data_length === data.length && last_width_column_count === column_count && $df_state.sort_state.sort_columns.length > 0) {
364
- return;
365
- }
366
- if (!parent) {
367
- return;
368
- }
369
- last_width_data_length = data.length;
370
- last_width_column_count = column_count;
371
- const widths = cells.map((el) => el?.clientWidth || 0);
372
- if (widths.length === 0) return;
373
- if (show_row_numbers) {
374
- parent.style.setProperty(`--cell-width-row-number`, `${widths[0]}px`);
375
- }
376
- for (let i = 0; i < 50; i++) {
377
- if (!column_widths[i]) {
378
- parent.style.removeProperty(`--cell-width-${i}`);
379
- } else if (column_widths[i].endsWith("%")) {
380
- const percentage = parseFloat(column_widths[i]);
381
- const pixel_width = Math.floor(percentage / 100 * parent.clientWidth);
382
- parent.style.setProperty(`--cell-width-${i}`, `${pixel_width}px`);
383
- } else {
384
- parent.style.setProperty(`--cell-width-${i}`, column_widths[i]);
385
- }
386
- }
387
- widths.forEach((width, i) => {
388
- if (!column_widths[i]) {
389
- const calculated_width = `${Math.max(width, 45)}px`;
390
- parent.style.setProperty(`--cell-width-${i}`, calculated_width);
391
- }
392
- });
393
- }
394
- function get_cell_width(index) {
395
- return `var(--cell-width-${index})`;
396
- }
397
- let table_height = values.slice(0, max_height / values.length * 37).length * 37 + 37;
398
- let scrollbar_width = 0;
399
- function sort_data(_data, _display_value, _styling) {
400
- const result = sort_data_and_preserve_selection(
401
- _data,
402
- _display_value,
403
- _styling,
404
- $df_state.sort_state.sort_columns,
405
- selected,
406
- get_current_indices
407
- );
408
- data = result.data;
409
- selected = result.selected;
410
- }
411
- function filter_data(_data, _display_value, _styling) {
412
- const result = filter_data_and_preserve_selection(
413
- _data,
414
- _display_value,
415
- _styling,
416
- $df_state.filter_state.filter_columns,
417
- selected,
418
- get_current_indices,
419
- $df_state.filter_state.initial_data?.data,
420
- $df_state.filter_state.initial_data?.display_value,
421
- $df_state.filter_state.initial_data?.styling
422
- );
423
- data = result.data;
424
- selected = result.selected;
425
- }
426
- $: selected_index = !!selected && selected[0];
427
- let is_visible = false;
428
- const set_copy_flash = (value) => {
429
- df_actions.set_copy_flash(value);
430
- if (value) {
431
- setTimeout(() => df_actions.set_copy_flash(false), 800);
432
- }
433
- };
434
- let previous_selected_cells = [];
435
- $: {
436
- if (copy_flash && !dequal(selected_cells, previous_selected_cells)) {
437
- set_copy_flash(false);
438
- }
439
- previous_selected_cells = selected_cells;
440
- }
441
- function handle_blur(event) {
442
- const { blur_event, coords } = event.detail;
443
- handle_cell_blur(blur_event, df_ctx, coords);
444
- }
445
- function toggle_header_menu(event, col) {
446
- event.stopPropagation();
447
- if (active_header_menu && active_header_menu.col === col) {
448
- df_actions.set_active_header_menu(null);
449
- } else {
450
- const header = event.target.closest("th");
451
- if (header) {
452
- const rect = header.getBoundingClientRect();
453
- df_actions.set_active_header_menu({
454
- col,
455
- x: rect.right,
456
- y: rect.bottom
457
- });
458
- }
459
- }
460
- }
461
- afterUpdate(() => {
462
- value_is_output = false;
463
- });
464
- function delete_col_at(index) {
465
- if (col_count[1] !== "dynamic") return;
466
- if (data[0].length <= 1) return;
467
- const result = df_actions.delete_col_at(data, headers, index);
468
- data = result.data;
469
- headers = result.headers;
470
- _headers = make_headers(headers, col_count, els, make_id);
471
- df_actions.set_active_cell_menu(null);
472
- df_actions.set_active_header_menu(null);
473
- df_actions.set_selected(false);
474
- df_actions.set_selected_cells([]);
475
- df_actions.set_editing(false);
476
- }
477
- function delete_row_at(index) {
478
- data = df_actions.delete_row_at(data, index);
479
- df_actions.set_active_cell_menu(null);
480
- df_actions.set_active_header_menu(null);
481
- }
482
- let selected_cell_coords;
483
- $: if (selected !== false) selected_cell_coords = selected;
484
- $: if (selected !== false) {
485
- const positions = calculate_selection_positions(
486
- selected,
487
- data,
488
- els,
489
- parent,
490
- table
491
- );
492
- document.documentElement.style.setProperty(
493
- "--selected-col-pos",
494
- positions.col_pos
495
- );
496
- document.documentElement.style.setProperty(
497
- "--selected-row-pos",
498
- positions.row_pos || "0px"
499
- );
500
- }
501
- function commit_filter() {
502
- if ($df_state.current_search_query && show_search === "filter") {
503
- const filtered_data = [];
504
- const filtered_display_values = [];
505
- const filtered_styling = [];
506
- search_results.forEach((row) => {
507
- const data_row = [];
508
- const display_row = [];
509
- const styling_row = [];
510
- row.forEach((cell) => {
511
- data_row.push(cell.value);
512
- display_row.push(
513
- cell.display_value !== void 0 ? cell.display_value : String(cell.value)
514
- );
515
- styling_row.push(cell.styling || "");
516
- });
517
- filtered_data.push(data_row);
518
- filtered_display_values.push(display_row);
519
- filtered_styling.push(styling_row);
520
- });
521
- const change_payload = {
522
- data: filtered_data,
523
- headers: _headers.map((h) => h.value),
524
- metadata: {
525
- display_value: filtered_display_values,
526
- styling: filtered_styling
527
- }
528
- };
529
- dispatch("change", change_payload);
530
- if (!value_is_output) {
531
- dispatch("input");
532
- }
533
- df_actions.handle_search(null);
534
- }
535
- }
536
- let viewport;
537
- let show_scroll_button = false;
538
- function scroll_to_top() {
539
- viewport.scrollTo({
540
- top: 0
541
- });
542
- }
543
- function handle_resize() {
544
- df_actions.set_active_cell_menu(null);
545
- df_actions.set_active_header_menu(null);
546
- selected_cells = [];
547
- selected = false;
548
- editing = false;
549
- width_calculated = false;
550
- set_cell_widths();
551
- }
552
- function add_row_at(index, position) {
553
- const row_index = position === "above" ? index : index + 1;
554
- add_row(row_index);
555
- active_cell_menu = null;
556
- active_header_menu = null;
557
- }
558
- function add_col_at(index, position) {
559
- const col_index = position === "left" ? index : index + 1;
560
- add_col(col_index);
561
- active_cell_menu = null;
562
- active_header_menu = null;
563
- }
564
- export function reset_sort_state() {
565
- df_actions.reset_sort_state();
566
- }
567
- function handle_select_all(col, checked) {
568
- data = data.map((row) => {
569
- const new_row = [...row];
570
- if (new_row[col]) {
571
- new_row[col] = {
572
- ...new_row[col],
573
- value: checked.toString()
574
- };
575
- }
576
- return new_row;
577
- });
578
- }
579
- let is_dragging = false;
580
- let drag_start = null;
581
- let mouse_down_pos = null;
582
- const drag_state = {
583
- is_dragging,
584
- drag_start,
585
- mouse_down_pos
586
- };
587
- $: {
588
- is_dragging = drag_state.is_dragging;
589
- drag_start = drag_state.drag_start;
590
- mouse_down_pos = drag_state.mouse_down_pos;
591
- }
592
- let drag_handlers;
593
- function init_drag_handlers() {
594
- drag_handlers = create_drag_handlers(
595
- drag_state,
596
- (value) => is_dragging = value,
597
- (cells2) => df_actions.set_selected_cells(cells2),
598
- (cell) => df_actions.set_selected(cell),
599
- (event, row, col) => df_actions.handle_cell_click(event, row, col),
600
- show_row_numbers,
601
- parent
602
- );
603
- }
604
- $: if (parent) init_drag_handlers();
605
- $: handle_mouse_down = drag_handlers?.handle_mouse_down || (() => {
606
- });
607
- $: handle_mouse_move = drag_handlers?.handle_mouse_move || (() => {
608
- });
609
- $: handle_mouse_up = drag_handlers?.handle_mouse_up || (() => {
610
- });
611
- function get_cell_display_value(row, col) {
612
- const is_search_active = $df_state.current_search_query !== void 0;
613
- if (is_search_active && search_results?.[row]?.[col]) {
614
- return search_results[row][col].display_value !== void 0 ? search_results[row][col].display_value : String(search_results[row][col].value);
615
- }
616
- if (data?.[row]?.[col]) {
617
- return data[row][col].display_value !== void 0 ? data[row][col].display_value : String(data[row][col].value);
618
- }
619
- return "";
620
- }
9
+ <script lang="ts">
10
+ import { afterUpdate, createEventDispatcher, tick, onMount } from "svelte";
11
+ import { dequal } from "dequal/lite";
12
+ import { Upload } from "@gradio/upload";
13
+
14
+ import EditableCell from "./EditableCell.svelte";
15
+ import RowNumber from "./RowNumber.svelte";
16
+ import TableHeader from "./TableHeader.svelte";
17
+ import TableCell from "./TableCell.svelte";
18
+ import EmptyRowButton from "./EmptyRowButton.svelte";
19
+ import type { SelectData } from "@gradio/utils";
20
+ import type { I18nFormatter } from "js/core/src/gradio_helper";
21
+ import { type Client } from "@gradio/client";
22
+ import VirtualTable from "./VirtualTable.svelte";
23
+ import type {
24
+ Headers,
25
+ DataframeValue,
26
+ Datatype,
27
+ EditData
28
+ } from "./utils/utils";
29
+ import CellMenu from "./CellMenu.svelte";
30
+ import Toolbar from "./Toolbar.svelte";
31
+ import type { CellCoordinate, CellValue } from "./types";
32
+ import {
33
+ is_cell_selected,
34
+ should_show_cell_menu,
35
+ get_current_indices,
36
+ handle_click_outside as handle_click_outside_util,
37
+ calculate_selection_positions
38
+ } from "./utils/selection_utils";
39
+ import {
40
+ copy_table_data,
41
+ get_max,
42
+ handle_file_upload
43
+ } from "./utils/table_utils";
44
+ import { make_headers, process_data } from "./utils/data_processing";
45
+ import { handle_keydown, handle_cell_blur } from "./utils/keyboard_utils";
46
+ import {
47
+ create_drag_handlers,
48
+ type DragState,
49
+ type DragHandlers
50
+ } from "./utils/drag_utils";
51
+ import { sort_data_and_preserve_selection } from "./utils/sort_utils";
52
+ import { filter_data_and_preserve_selection } from "./utils/filter_utils";
53
+
54
+ export let datatype: Datatype | Datatype[];
55
+ export let label: string | null = null;
56
+ export let show_label = true;
57
+ export let headers: Headers = [];
58
+ export let values: CellValue[][] = [];
59
+ export let col_count: [number, "fixed" | "dynamic"];
60
+ export let row_count: [number, "fixed" | "dynamic"];
61
+ export let latex_delimiters: {
62
+ left: string;
63
+ right: string;
64
+ display: boolean;
65
+ }[];
66
+ export let components: Record<string, any> = {};
67
+
68
+ export let editable = true;
69
+ export let wrap = false;
70
+ export let root: string;
71
+ export let i18n: I18nFormatter;
72
+
73
+ export let max_height = 500;
74
+ export let line_breaks = true;
75
+ export let column_widths: string[] = [];
76
+ export let show_row_numbers = false;
77
+ export let upload: Client["upload"];
78
+ export let stream_handler: Client["stream"];
79
+ export let buttons: string[] | null = null;
80
+ export let value_is_output = false;
81
+ export let max_chars: number | undefined = undefined;
82
+ export let show_search: "none" | "search" | "filter" = "none";
83
+ export let pinned_columns = 0;
84
+ export let static_columns: (string | number)[] = [];
85
+ export let fullscreen = false;
86
+
87
+ const df_ctx = create_dataframe_context({
88
+ show_fullscreen_button:
89
+ buttons === null ? true : buttons.includes("fullscreen"),
90
+ show_copy_button: buttons === null ? true : buttons.includes("copy"),
91
+ show_search,
92
+ show_row_numbers,
93
+ editable,
94
+ pinned_columns,
95
+ show_label,
96
+ line_breaks,
97
+ wrap,
98
+ max_height,
99
+ column_widths,
100
+ max_chars,
101
+ static_columns
102
+ });
103
+
104
+ const { state: df_state, actions: df_actions } = df_ctx;
105
+
106
+ $: selected_cells = $df_state.ui_state.selected_cells;
107
+ $: selected = $df_state.ui_state.selected;
108
+ $: editing = $df_state.ui_state.editing;
109
+ $: header_edit = $df_state.ui_state.header_edit;
110
+ $: selected_header = $df_state.ui_state.selected_header;
111
+ $: active_cell_menu = $df_state.ui_state.active_cell_menu;
112
+ $: active_header_menu = $df_state.ui_state.active_header_menu;
113
+ $: copy_flash = $df_state.ui_state.copy_flash;
114
+
115
+ $: actual_pinned_columns =
116
+ pinned_columns && data?.[0]?.length
117
+ ? Math.min(pinned_columns, data[0].length)
118
+ : 0;
119
+
120
+ onMount(() => {
121
+ df_ctx.parent_element = parent;
122
+ df_ctx.get_data_at = get_data_at;
123
+ df_ctx.get_column = get_column;
124
+ df_ctx.get_row = get_row;
125
+ df_ctx.dispatch = dispatch;
126
+ init_drag_handlers();
127
+
128
+ const observer = new IntersectionObserver((entries) => {
129
+ entries.forEach((entry) => {
130
+ if (entry.isIntersecting && !is_visible) {
131
+ width_calculated = false;
132
+ }
133
+ is_visible = entry.isIntersecting;
134
+ });
135
+ });
136
+ observer.observe(parent);
137
+ document.addEventListener("click", handle_click_outside);
138
+ window.addEventListener("resize", handle_resize);
139
+
140
+ const global_mouse_up = (event: MouseEvent): void => {
141
+ if (is_dragging || drag_start) {
142
+ handle_mouse_up(event);
143
+ }
144
+ };
145
+ document.addEventListener("mouseup", global_mouse_up);
146
+
147
+ return () => {
148
+ observer.disconnect();
149
+ document.removeEventListener("click", handle_click_outside);
150
+ window.removeEventListener("resize", handle_resize);
151
+ document.removeEventListener("mouseup", global_mouse_up);
152
+ };
153
+ });
154
+
155
+ $: {
156
+ if (data || _headers || els) {
157
+ df_ctx.data = data;
158
+ df_ctx.headers = _headers;
159
+ df_ctx.els = els;
160
+ df_ctx.display_value = display_value;
161
+ df_ctx.styling = styling;
162
+ }
163
+ }
164
+
165
+ const dispatch = createEventDispatcher<{
166
+ change: DataframeValue;
167
+ input: undefined;
168
+ select: SelectData;
169
+ search: string | null;
170
+ edit: EditData;
171
+ }>();
172
+
173
+ let els: Record<
174
+ string,
175
+ { cell: null | HTMLTableCellElement; input: null | HTMLTextAreaElement }
176
+ > = {};
177
+ let data_binding: Record<string, (typeof data)[0][0]> = {};
178
+ let _headers = make_headers(headers, col_count, els, make_id);
179
+ let old_headers: string[] = headers;
180
+ let data: { id: string; value: CellValue; display_value?: string }[][] = [[]];
181
+ let old_val: undefined | CellValue[][] = undefined;
182
+ let search_results: {
183
+ id: string;
184
+ value: CellValue;
185
+ display_value?: string;
186
+ styling?: string;
187
+ }[][] = [[]];
188
+ let dragging = false;
189
+ let color_accent_copied: string;
190
+ let filtered_to_original_map: number[] = [];
191
+
192
+ onMount(() => {
193
+ const color = getComputedStyle(document.documentElement)
194
+ .getPropertyValue("--color-accent")
195
+ .trim();
196
+ color_accent_copied = color + "40"; // 80 is 50% opacity in hex
197
+ document.documentElement.style.setProperty(
198
+ "--color-accent-copied",
199
+ color_accent_copied
200
+ );
201
+ });
202
+
203
+ const get_data_at = (row: number, col: number): CellValue =>
204
+ data?.[row]?.[col]?.value;
205
+
206
+ const get_column = (col: number): CellValue[] =>
207
+ data?.map((row) => row[col]?.value) ?? [];
208
+
209
+ const get_row = (row: number): CellValue[] =>
210
+ data?.[row]?.map((cell) => cell.value) ?? [];
211
+
212
+ $: {
213
+ if (!dequal(headers, old_headers)) {
214
+ _headers = make_headers(headers, col_count, els, make_id);
215
+ old_headers = JSON.parse(JSON.stringify(headers));
216
+ }
217
+ }
218
+
219
+ function make_id(): string {
220
+ return Math.random().toString(36).substring(2, 15);
221
+ }
222
+
223
+ export let display_value: string[][] | null = null;
224
+ export let styling: string[][] | null = null;
225
+
226
+ $: if (!dequal(values, old_val)) {
227
+ if (parent) {
228
+ // only clear column widths when the data structure changes
229
+ const is_reset =
230
+ values.length === 0 || (values.length === 1 && values[0].length === 0);
231
+ const is_different_structure =
232
+ old_val !== undefined &&
233
+ (values.length !== old_val.length ||
234
+ (values[0] && old_val[0] && values[0].length !== old_val[0].length));
235
+
236
+ if (is_reset || is_different_structure) {
237
+ for (let i = 0; i < 50; i++) {
238
+ parent.style.removeProperty(`--cell-width-${i}`);
239
+ }
240
+ last_width_data_length = 0;
241
+ last_width_column_count = 0;
242
+ width_calculated = false;
243
+ }
244
+ }
245
+
246
+ // only reset sort state when values are changed
247
+ const is_reset =
248
+ values.length === 0 || (values.length === 1 && values[0].length === 0);
249
+ const is_different_structure =
250
+ old_val !== undefined &&
251
+ (values.length !== old_val.length ||
252
+ (values[0] && old_val[0] && values[0].length !== old_val[0].length));
253
+
254
+ data = process_data(
255
+ values as CellValue[][],
256
+ els,
257
+ data_binding,
258
+ make_id,
259
+ display_value,
260
+ datatype
261
+ );
262
+ old_val = JSON.parse(JSON.stringify(values)) as CellValue[][];
263
+
264
+ if (is_reset || is_different_structure) {
265
+ df_actions.reset_sort_state();
266
+ } else if ($df_state.sort_state.sort_columns.length > 0) {
267
+ sort_data(data, display_value, styling);
268
+ } else {
269
+ df_actions.handle_sort(-1, "asc");
270
+ df_actions.reset_sort_state();
271
+ }
272
+
273
+ if ($df_state.filter_state.filter_columns.length > 0) {
274
+ filter_data(data, display_value, styling);
275
+ } else {
276
+ df_actions.reset_filter_state();
277
+ }
278
+
279
+ if ($df_state.current_search_query) {
280
+ df_actions.handle_search(null);
281
+ }
282
+
283
+ if (parent && cells.length > 0 && (is_reset || is_different_structure)) {
284
+ width_calculated = false;
285
+ }
286
+ }
287
+
288
+ $: if ($df_state.current_search_query !== undefined) {
289
+ const cell_map = new Map();
290
+ filtered_to_original_map = [];
291
+
292
+ data.forEach((row, row_idx) => {
293
+ if (
294
+ row.some((cell) =>
295
+ String(cell?.value)
296
+ .toLowerCase()
297
+ .includes($df_state.current_search_query?.toLowerCase() || "")
298
+ )
299
+ ) {
300
+ filtered_to_original_map.push(row_idx);
301
+ }
302
+ row.forEach((cell, col_idx) => {
303
+ cell_map.set(cell.id, {
304
+ value: cell.value,
305
+ display_value:
306
+ cell.display_value !== undefined
307
+ ? cell.display_value
308
+ : String(cell.value),
309
+ styling: styling?.[row_idx]?.[col_idx] || ""
310
+ });
311
+ });
312
+ });
313
+
314
+ const filtered = df_actions.filter_data(data);
315
+
316
+ search_results = filtered.map((row) =>
317
+ row.map((cell) => {
318
+ const original = cell_map.get(cell.id);
319
+ return {
320
+ ...cell,
321
+ display_value:
322
+ original?.display_value !== undefined
323
+ ? original.display_value
324
+ : String(cell.value),
325
+ styling: original?.styling || ""
326
+ };
327
+ })
328
+ );
329
+ } else {
330
+ filtered_to_original_map = [];
331
+ }
332
+
333
+ let previous_headers = _headers.map((h) => h.value);
334
+ let previous_data = data.map((row) => row.map((cell) => cell.value));
335
+
336
+ $: {
337
+ if (data || _headers) {
338
+ df_actions.trigger_change(
339
+ data,
340
+ _headers,
341
+ previous_data,
342
+ previous_headers,
343
+ value_is_output,
344
+ dispatch
345
+ );
346
+ previous_data = data.map((row) => row.map((cell) => cell.value));
347
+ previous_headers = _headers.map((h) => h.value);
348
+ }
349
+ }
350
+
351
+ function handle_sort(col: number, direction: SortDirection): void {
352
+ df_actions.handle_sort(col, direction);
353
+ sort_data(data, display_value, styling);
354
+ }
355
+
356
+ function clear_sort(): void {
357
+ df_actions.reset_sort_state();
358
+ sort_data(data, display_value, styling);
359
+ }
360
+
361
+ $: {
362
+ if ($df_state.filter_state.filter_columns.length > 0) {
363
+ filter_data(data, display_value, styling);
364
+ }
365
+
366
+ if ($df_state.sort_state.sort_columns.length > 0) {
367
+ sort_data(data, display_value, styling);
368
+ df_actions.update_row_order(data);
369
+ }
370
+ }
371
+
372
+ function handle_filter(
373
+ col: number,
374
+ datatype: FilterDatatype,
375
+ filter: string,
376
+ value: string
377
+ ): void {
378
+ df_actions.handle_filter(col, datatype, filter, value);
379
+ filter_data(data, display_value, styling);
380
+ }
381
+
382
+ function clear_filter(): void {
383
+ df_actions.reset_filter_state();
384
+ filter_data(data, display_value, styling);
385
+ }
386
+
387
+ async function edit_header(i: number, _select = false): Promise<void> {
388
+ if (!editable || header_edit === i || col_count[1] !== "dynamic") return;
389
+ df_actions.set_header_edit(i);
390
+ }
391
+
392
+ function handle_header_click(event: MouseEvent, col: number): void {
393
+ if (event.target instanceof HTMLAnchorElement) {
394
+ return;
395
+ }
396
+ event.preventDefault();
397
+ event.stopPropagation();
398
+ if (!editable) return;
399
+ df_actions.set_editing(false);
400
+ df_actions.handle_header_click(col, editable);
401
+ parent.focus();
402
+ }
403
+
404
+ function end_header_edit(event: CustomEvent<KeyboardEvent>): void {
405
+ if (!editable) return;
406
+ df_actions.end_header_edit(event.detail.key);
407
+ parent.focus();
408
+ }
409
+
410
+ async function add_row(index?: number): Promise<void> {
411
+ parent.focus();
412
+
413
+ if (row_count[1] !== "dynamic") return;
414
+
415
+ const new_row = Array(data[0]?.length || headers.length)
416
+ .fill(0)
417
+ .map((_, i) => {
418
+ const _id = make_id();
419
+ els[_id] = { cell: null, input: null };
420
+ return { id: _id, value: "" };
421
+ });
422
+
423
+ if (data.length === 0) {
424
+ data = [new_row];
425
+ } else if (index !== undefined && index >= 0 && index <= data.length) {
426
+ data.splice(index, 0, new_row);
427
+ } else {
428
+ data.push(new_row);
429
+ }
430
+
431
+ selected = [index !== undefined ? index : data.length - 1, 0];
432
+ }
433
+
434
+ async function add_col(index?: number): Promise<void> {
435
+ parent.focus();
436
+ if (col_count[1] !== "dynamic") return;
437
+
438
+ const result = df_actions.add_col(data, headers, make_id, index);
439
+
440
+ result.data.forEach((row) => {
441
+ row.forEach((cell) => {
442
+ if (!els[cell.id]) {
443
+ els[cell.id] = { cell: null, input: null };
444
+ }
445
+ });
446
+ });
447
+
448
+ data = result.data;
449
+ headers = result.headers;
450
+
451
+ await tick();
452
+
453
+ requestAnimationFrame(() => {
454
+ edit_header(index !== undefined ? index : data[0].length - 1, true);
455
+ const new_w = parent.querySelectorAll("tbody")[1].offsetWidth;
456
+ parent.querySelectorAll("table")[1].scrollTo({ left: new_w });
457
+ });
458
+ }
459
+
460
+ function handle_click_outside(event: Event): void {
461
+ if (handle_click_outside_util(event, parent)) {
462
+ df_actions.clear_ui_state();
463
+ header_edit = false;
464
+ selected_header = false;
465
+ }
466
+ }
467
+
468
+ $: max = get_max(data);
469
+
470
+ let width_calc_timeout: ReturnType<typeof setTimeout>;
471
+ $: if (cells[0] && cells[0]?.clientWidth) {
472
+ clearTimeout(width_calc_timeout);
473
+ width_calc_timeout = setTimeout(() => set_cell_widths(), 100);
474
+ }
475
+
476
+ let width_calculated = false;
477
+ $: if (cells[0] && !width_calculated) {
478
+ set_cell_widths();
479
+ width_calculated = true;
480
+ }
481
+ let cells: HTMLTableCellElement[] = [];
482
+ let parent: HTMLDivElement;
483
+ let table: HTMLTableElement;
484
+ let last_width_data_length = 0;
485
+ let last_width_column_count = 0;
486
+
487
+ function set_cell_widths(): void {
488
+ const column_count = data[0]?.length || 0;
489
+ if ($df_state.filter_state.filter_columns.length > 0) {
490
+ return;
491
+ }
492
+ if (
493
+ last_width_data_length === data.length &&
494
+ last_width_column_count === column_count &&
495
+ $df_state.sort_state.sort_columns.length > 0
496
+ ) {
497
+ return;
498
+ }
499
+
500
+ if (!parent) {
501
+ return;
502
+ }
503
+
504
+ last_width_data_length = data.length;
505
+ last_width_column_count = column_count;
506
+
507
+ const widths = cells.map((el) => el?.clientWidth || 0);
508
+ if (widths.length === 0) return;
509
+
510
+ if (show_row_numbers) {
511
+ parent.style.setProperty(`--cell-width-row-number`, `${widths[0]}px`);
512
+ }
513
+
514
+ for (let i = 0; i < 50; i++) {
515
+ if (!column_widths[i]) {
516
+ parent.style.removeProperty(`--cell-width-${i}`);
517
+ } else if (column_widths[i].endsWith("%")) {
518
+ const percentage = parseFloat(column_widths[i]);
519
+ const pixel_width = Math.floor((percentage / 100) * parent.clientWidth);
520
+ parent.style.setProperty(`--cell-width-${i}`, `${pixel_width}px`);
521
+ } else {
522
+ parent.style.setProperty(`--cell-width-${i}`, column_widths[i]);
523
+ }
524
+ }
525
+
526
+ widths.forEach((width, i) => {
527
+ if (!column_widths[i]) {
528
+ const calculated_width = `${Math.max(width, 45)}px`;
529
+ parent.style.setProperty(`--cell-width-${i}`, calculated_width);
530
+ }
531
+ });
532
+ }
533
+
534
+ function get_cell_width(index: number): string {
535
+ return `var(--cell-width-${index})`;
536
+ }
537
+
538
+ let table_height: number =
539
+ values.slice(0, (max_height / values.length) * 37).length * 37 + 37;
540
+ let scrollbar_width = 0;
541
+
542
+ function sort_data(
543
+ _data: typeof data,
544
+ _display_value: string[][] | null,
545
+ _styling: string[][] | null
546
+ ): void {
547
+ const result = sort_data_and_preserve_selection(
548
+ _data,
549
+ _display_value,
550
+ _styling,
551
+ $df_state.sort_state.sort_columns,
552
+ selected,
553
+ get_current_indices
554
+ );
555
+
556
+ data = result.data;
557
+ selected = result.selected;
558
+ }
559
+
560
+ function filter_data(
561
+ _data: typeof data,
562
+ _display_value: string[][] | null,
563
+ _styling: string[][] | null
564
+ ): void {
565
+ const result = filter_data_and_preserve_selection(
566
+ _data,
567
+ _display_value,
568
+ _styling,
569
+ $df_state.filter_state.filter_columns,
570
+ selected,
571
+ get_current_indices,
572
+ $df_state.filter_state.initial_data?.data,
573
+ $df_state.filter_state.initial_data?.display_value,
574
+ $df_state.filter_state.initial_data?.styling
575
+ );
576
+ data = result.data;
577
+ selected = result.selected;
578
+ }
579
+
580
+ $: selected_index = !!selected && selected[0];
581
+
582
+ let is_visible = false;
583
+
584
+ const set_copy_flash = (value: boolean): void => {
585
+ df_actions.set_copy_flash(value);
586
+ if (value) {
587
+ setTimeout(() => df_actions.set_copy_flash(false), 800);
588
+ }
589
+ };
590
+
591
+ let previous_selected_cells: [number, number][] = [];
592
+
593
+ $: {
594
+ if (copy_flash && !dequal(selected_cells, previous_selected_cells)) {
595
+ set_copy_flash(false);
596
+ }
597
+ previous_selected_cells = selected_cells;
598
+ }
599
+
600
+ function handle_blur(
601
+ event: CustomEvent<{
602
+ blur_event: FocusEvent;
603
+ coords: [number, number];
604
+ }>
605
+ ): void {
606
+ const { blur_event, coords } = event.detail;
607
+ handle_cell_blur(blur_event, df_ctx, coords);
608
+ }
609
+
610
+ function toggle_header_menu(event: MouseEvent, col: number): void {
611
+ event.stopPropagation();
612
+ if (active_header_menu && active_header_menu.col === col) {
613
+ df_actions.set_active_header_menu(null);
614
+ } else {
615
+ const header = (event.target as HTMLElement).closest("th");
616
+ if (header) {
617
+ const rect = header.getBoundingClientRect();
618
+ df_actions.set_active_header_menu({
619
+ col,
620
+ x: rect.right,
621
+ y: rect.bottom
622
+ });
623
+ }
624
+ }
625
+ }
626
+
627
+ afterUpdate(() => {
628
+ value_is_output = false;
629
+ });
630
+
631
+ function delete_col_at(index: number): void {
632
+ if (col_count[1] !== "dynamic") return;
633
+ if (data[0].length <= 1) return;
634
+
635
+ const result = df_actions.delete_col_at(data, headers, index);
636
+ data = result.data;
637
+ headers = result.headers;
638
+ _headers = make_headers(headers, col_count, els, make_id);
639
+ df_actions.set_active_cell_menu(null);
640
+ df_actions.set_active_header_menu(null);
641
+ df_actions.set_selected(false);
642
+ df_actions.set_selected_cells([]);
643
+ df_actions.set_editing(false);
644
+ }
645
+
646
+ function delete_row_at(index: number): void {
647
+ data = df_actions.delete_row_at(data, index);
648
+ df_actions.set_active_cell_menu(null);
649
+ df_actions.set_active_header_menu(null);
650
+ }
651
+
652
+ let selected_cell_coords: CellCoordinate;
653
+ $: if (selected !== false) selected_cell_coords = selected;
654
+
655
+ $: if (selected !== false) {
656
+ const positions = calculate_selection_positions(
657
+ selected,
658
+ data,
659
+ els,
660
+ parent,
661
+ table
662
+ );
663
+ document.documentElement.style.setProperty(
664
+ "--selected-col-pos",
665
+ positions.col_pos
666
+ );
667
+ document.documentElement.style.setProperty(
668
+ "--selected-row-pos",
669
+ positions.row_pos || "0px"
670
+ );
671
+ }
672
+
673
+ function commit_filter(): void {
674
+ if ($df_state.current_search_query && show_search === "filter") {
675
+ const filtered_data: CellValue[][] = [];
676
+ const filtered_display_values: string[][] = [];
677
+ const filtered_styling: string[][] = [];
678
+
679
+ search_results.forEach((row) => {
680
+ const data_row: CellValue[] = [];
681
+ const display_row: string[] = [];
682
+ const styling_row: string[] = [];
683
+
684
+ row.forEach((cell) => {
685
+ data_row.push(cell.value);
686
+ display_row.push(
687
+ cell.display_value !== undefined
688
+ ? cell.display_value
689
+ : String(cell.value)
690
+ );
691
+ styling_row.push(cell.styling || "");
692
+ });
693
+
694
+ filtered_data.push(data_row);
695
+ filtered_display_values.push(display_row);
696
+ filtered_styling.push(styling_row);
697
+ });
698
+
699
+ const change_payload = {
700
+ data: filtered_data,
701
+ headers: _headers.map((h) => h.value),
702
+ metadata: {
703
+ display_value: filtered_display_values,
704
+ styling: filtered_styling
705
+ }
706
+ };
707
+
708
+ dispatch("change", change_payload);
709
+
710
+ if (!value_is_output) {
711
+ dispatch("input");
712
+ }
713
+
714
+ df_actions.handle_search(null);
715
+ }
716
+ }
717
+
718
+ let viewport: HTMLTableElement;
719
+ let show_scroll_button = false;
720
+
721
+ function scroll_to_top(): void {
722
+ viewport.scrollTo({
723
+ top: 0
724
+ });
725
+ }
726
+
727
+ function handle_resize(): void {
728
+ df_actions.set_active_cell_menu(null);
729
+ df_actions.set_active_header_menu(null);
730
+ selected_cells = [];
731
+ selected = false;
732
+ editing = false;
733
+ width_calculated = false;
734
+ set_cell_widths();
735
+ }
736
+
737
+ function add_row_at(index: number, position: "above" | "below"): void {
738
+ const row_index = position === "above" ? index : index + 1;
739
+ add_row(row_index);
740
+ active_cell_menu = null;
741
+ active_header_menu = null;
742
+ }
743
+
744
+ function add_col_at(index: number, position: "left" | "right"): void {
745
+ const col_index = position === "left" ? index : index + 1;
746
+ add_col(col_index);
747
+ active_cell_menu = null;
748
+ active_header_menu = null;
749
+ }
750
+
751
+ export function reset_sort_state(): void {
752
+ df_actions.reset_sort_state();
753
+ }
754
+
755
+ function handle_select_all(col: number, checked: boolean): void {
756
+ data = data.map((row) => {
757
+ const new_row = [...row];
758
+ if (new_row[col]) {
759
+ new_row[col] = {
760
+ ...new_row[col],
761
+ value: checked
762
+ };
763
+ }
764
+ return new_row;
765
+ });
766
+ }
767
+
768
+ let is_dragging = false;
769
+ let drag_start: [number, number] | null = null;
770
+ let mouse_down_pos: { x: number; y: number } | null = null;
771
+
772
+ const drag_state: DragState = {
773
+ is_dragging,
774
+ drag_start,
775
+ mouse_down_pos
776
+ };
777
+
778
+ $: {
779
+ is_dragging = drag_state.is_dragging;
780
+ drag_start = drag_state.drag_start;
781
+ mouse_down_pos = drag_state.mouse_down_pos;
782
+ }
783
+
784
+ let drag_handlers: DragHandlers;
785
+
786
+ function init_drag_handlers(): void {
787
+ drag_handlers = create_drag_handlers(
788
+ drag_state,
789
+ (value) => (is_dragging = value),
790
+ (cells) => df_actions.set_selected_cells(cells),
791
+ (cell) => df_actions.set_selected(cell),
792
+ (event, row, col) => df_actions.handle_cell_click(event, row, col),
793
+ show_row_numbers,
794
+ parent
795
+ );
796
+ }
797
+
798
+ $: if (parent) init_drag_handlers();
799
+
800
+ $: handle_mouse_down = drag_handlers?.handle_mouse_down || (() => {});
801
+ $: handle_mouse_move = drag_handlers?.handle_mouse_move || (() => {});
802
+ $: handle_mouse_up = drag_handlers?.handle_mouse_up || (() => {});
803
+
804
+ function get_cell_display_value(row: number, col: number): string {
805
+ const is_search_active = $df_state.current_search_query !== undefined;
806
+
807
+ if (is_search_active && search_results?.[row]?.[col]) {
808
+ return search_results[row][col].display_value !== undefined
809
+ ? search_results[row][col].display_value
810
+ : String(search_results[row][col].value);
811
+ }
812
+
813
+ if (data?.[row]?.[col]) {
814
+ return data[row][col].display_value !== undefined
815
+ ? data[row][col].display_value
816
+ : String(data[row][col].value);
817
+ }
818
+
819
+ return "";
820
+ }
621
821
  </script>
622
822
 
623
823
  <svelte:window on:resize={() => set_cell_widths()} />
624
824
 
625
825
  <div class="table-container">
626
- {#if (label && label.length !== 0 && show_label) || show_fullscreen_button || show_copy_button || show_search !== "none"}
826
+ {#if (label && label.length !== 0 && show_label) || (buttons === null ? true : buttons.includes("fullscreen")) || (buttons === null ? true : buttons.includes("copy")) || show_search !== "none"}
627
827
  <div class="header-row">
628
828
  {#if label && label.length !== 0 && show_label}
629
829
  <div class="label">
@@ -631,10 +831,12 @@ function get_cell_display_value(row, col) {
631
831
  </div>
632
832
  {/if}
633
833
  <Toolbar
634
- {show_fullscreen_button}
834
+ show_fullscreen_button={buttons === null
835
+ ? true
836
+ : buttons.includes("fullscreen")}
635
837
  {fullscreen}
636
838
  on_copy={async () => await copy_table_data(data, null)}
637
- {show_copy_button}
839
+ show_copy_button={buttons === null ? true : buttons.includes("copy")}
638
840
  {show_search}
639
841
  on:search={(e) => df_actions.handle_search(e.detail)}
640
842
  on:fullscreen
@@ -858,7 +1060,7 @@ function get_cell_display_value(row, col) {
858
1060
  <CellMenu
859
1061
  x={active_cell_menu?.x ?? active_header_menu?.x ?? 0}
860
1062
  y={active_cell_menu?.y ?? active_header_menu?.y ?? 0}
861
- row={active_header_menu ? -1 : active_cell_menu?.row ?? 0}
1063
+ row={active_header_menu ? -1 : (active_cell_menu?.row ?? 0)}
862
1064
  {col_count}
863
1065
  {row_count}
864
1066
  on_add_row_above={() => add_row_at(active_cell_menu?.row ?? -1, "above")}
@@ -895,9 +1097,9 @@ function get_cell_display_value(row, col) {
895
1097
  }
896
1098
  : undefined}
897
1099
  sort_direction={active_header_menu
898
- ? $df_state.sort_state.sort_columns.find(
1100
+ ? ($df_state.sort_state.sort_columns.find(
899
1101
  (item) => item.col === (active_header_menu?.col ?? -1)
900
- )?.direction ?? null
1102
+ )?.direction ?? null)
901
1103
  : null}
902
1104
  sort_priority={active_header_menu
903
1105
  ? $df_state.sort_state.sort_columns.findIndex(