@gradio/dataframe 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/Dataframe.stories.svelte +168 -2
- package/Index.svelte +20 -3
- package/dist/Index.svelte +16 -4
- package/dist/Index.svelte.d.ts +12 -0
- package/dist/shared/EditableCell.svelte +1 -4
- package/dist/shared/Table.svelte +423 -181
- package/dist/shared/Table.svelte.d.ts +3 -0
- package/dist/shared/Toolbar.svelte +122 -30
- package/dist/shared/Toolbar.svelte.d.ts +4 -0
- package/dist/shared/VirtualTable.svelte +70 -26
- package/dist/shared/VirtualTable.svelte.d.ts +1 -0
- package/dist/shared/icons/FilterIcon.svelte +11 -0
- package/dist/shared/icons/FilterIcon.svelte.d.ts +16 -0
- package/dist/shared/icons/SortIcon.svelte +90 -0
- package/dist/shared/icons/SortIcon.svelte.d.ts +20 -0
- package/dist/shared/selection_utils.d.ts +12 -2
- package/dist/shared/selection_utils.js +33 -5
- package/dist/shared/types.d.ts +16 -0
- package/dist/shared/types.js +1 -0
- package/dist/shared/utils/menu_utils.d.ts +42 -0
- package/dist/shared/utils/menu_utils.js +58 -0
- package/dist/shared/utils/sort_utils.d.ts +7 -0
- package/dist/shared/utils/sort_utils.js +39 -0
- package/dist/shared/utils/table_utils.d.ts +12 -0
- package/dist/shared/utils/table_utils.js +148 -0
- package/package.json +8 -8
- package/shared/EditableCell.svelte +1 -4
- package/shared/Table.svelte +453 -182
- package/shared/Toolbar.svelte +125 -30
- package/shared/VirtualTable.svelte +73 -26
- package/shared/icons/FilterIcon.svelte +12 -0
- package/shared/icons/SortIcon.svelte +95 -0
- package/shared/selection_utils.ts +51 -9
- package/shared/types.ts +27 -0
- package/shared/utils/menu_utils.ts +115 -0
- package/shared/utils/sort_utils.test.ts +71 -0
- package/shared/utils/sort_utils.ts +55 -0
- package/shared/utils/table_utils.test.ts +114 -0
- package/shared/utils/table_utils.ts +206 -0
- package/dist/shared/table_utils.d.ts +0 -12
- package/dist/shared/table_utils.js +0 -113
- package/shared/table_utils.ts +0 -148
package/shared/Table.svelte
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
} from "./utils";
|
|
17
17
|
import CellMenu from "./CellMenu.svelte";
|
|
18
18
|
import Toolbar from "./Toolbar.svelte";
|
|
19
|
+
import SortIcon from "./icons/SortIcon.svelte";
|
|
19
20
|
import type { CellCoordinate, EditingState } from "./types";
|
|
20
21
|
import {
|
|
21
22
|
is_cell_selected,
|
|
@@ -26,9 +27,17 @@
|
|
|
26
27
|
get_range_selection,
|
|
27
28
|
move_cursor,
|
|
28
29
|
get_current_indices,
|
|
29
|
-
handle_click_outside as handle_click_outside_util
|
|
30
|
+
handle_click_outside as handle_click_outside_util,
|
|
31
|
+
select_column,
|
|
32
|
+
select_row,
|
|
33
|
+
calculate_selection_positions
|
|
30
34
|
} from "./selection_utils";
|
|
31
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
copy_table_data,
|
|
37
|
+
get_max,
|
|
38
|
+
handle_file_upload,
|
|
39
|
+
sort_table_data
|
|
40
|
+
} from "./utils/table_utils";
|
|
32
41
|
|
|
33
42
|
export let datatype: Datatype | Datatype[];
|
|
34
43
|
export let label: string | null = null;
|
|
@@ -58,6 +67,14 @@
|
|
|
58
67
|
export let show_copy_button = false;
|
|
59
68
|
export let value_is_output = false;
|
|
60
69
|
export let max_chars: number | undefined = undefined;
|
|
70
|
+
export let show_search: "none" | "search" | "filter" = "none";
|
|
71
|
+
export let pinned_columns = 0;
|
|
72
|
+
|
|
73
|
+
let actual_pinned_columns = 0;
|
|
74
|
+
$: actual_pinned_columns =
|
|
75
|
+
pinned_columns && data?.[0]?.length
|
|
76
|
+
? Math.min(pinned_columns, data[0].length)
|
|
77
|
+
: 0;
|
|
61
78
|
|
|
62
79
|
let selected_cells: CellCoordinate[] = [];
|
|
63
80
|
$: selected_cells = [...selected_cells];
|
|
@@ -80,6 +97,7 @@
|
|
|
80
97
|
change: DataframeValue;
|
|
81
98
|
input: undefined;
|
|
82
99
|
select: SelectData;
|
|
100
|
+
search: string | null;
|
|
83
101
|
}>();
|
|
84
102
|
|
|
85
103
|
let editing: EditingState = false;
|
|
@@ -99,6 +117,19 @@
|
|
|
99
117
|
} | null = null;
|
|
100
118
|
let is_fullscreen = false;
|
|
101
119
|
let dragging = false;
|
|
120
|
+
let copy_flash = false;
|
|
121
|
+
|
|
122
|
+
let color_accent_copied: string;
|
|
123
|
+
onMount(() => {
|
|
124
|
+
const color = getComputedStyle(document.documentElement)
|
|
125
|
+
.getPropertyValue("--color-accent")
|
|
126
|
+
.trim();
|
|
127
|
+
color_accent_copied = color + "40"; // 80 is 50% opacity in hex
|
|
128
|
+
document.documentElement.style.setProperty(
|
|
129
|
+
"--color-accent-copied",
|
|
130
|
+
color_accent_copied
|
|
131
|
+
);
|
|
132
|
+
});
|
|
102
133
|
|
|
103
134
|
const get_data_at = (row: number, col: number): string | number =>
|
|
104
135
|
data?.[row]?.[col]?.value;
|
|
@@ -107,7 +138,14 @@
|
|
|
107
138
|
return Math.random().toString(36).substring(2, 15);
|
|
108
139
|
}
|
|
109
140
|
|
|
110
|
-
function make_headers(
|
|
141
|
+
function make_headers(
|
|
142
|
+
_head: Headers,
|
|
143
|
+
col_count: [number, "fixed" | "dynamic"],
|
|
144
|
+
els: Record<
|
|
145
|
+
string,
|
|
146
|
+
{ cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
|
|
147
|
+
>
|
|
148
|
+
): HeadersWithIDs {
|
|
111
149
|
let _h = _head || [];
|
|
112
150
|
if (col_count[1] === "fixed" && _h.length < col_count[0]) {
|
|
113
151
|
const fill = Array(col_count[0] - _h.length)
|
|
@@ -140,8 +178,8 @@
|
|
|
140
178
|
const data_row_length = _values.length;
|
|
141
179
|
return Array(row_count[1] === "fixed" ? row_count[0] : data_row_length)
|
|
142
180
|
.fill(0)
|
|
143
|
-
.map((_, i) =>
|
|
144
|
-
Array(
|
|
181
|
+
.map((_, i) => {
|
|
182
|
+
return Array(
|
|
145
183
|
col_count[1] === "fixed"
|
|
146
184
|
? col_count[0]
|
|
147
185
|
: data_row_length > 0
|
|
@@ -155,16 +193,16 @@
|
|
|
155
193
|
const obj = { value: _values?.[i]?.[j] ?? "", id };
|
|
156
194
|
data_binding[id] = obj;
|
|
157
195
|
return obj;
|
|
158
|
-
})
|
|
159
|
-
);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
160
198
|
}
|
|
161
199
|
|
|
162
|
-
let _headers = make_headers(headers);
|
|
200
|
+
let _headers = make_headers(headers, col_count, els);
|
|
163
201
|
let old_headers: string[] = headers;
|
|
164
202
|
|
|
165
203
|
$: {
|
|
166
204
|
if (!dequal(headers, old_headers)) {
|
|
167
|
-
_headers = make_headers(headers);
|
|
205
|
+
_headers = make_headers(headers, col_count, els);
|
|
168
206
|
old_headers = JSON.parse(JSON.stringify(headers));
|
|
169
207
|
}
|
|
170
208
|
}
|
|
@@ -181,6 +219,8 @@
|
|
|
181
219
|
let previous_data = data.map((row) => row.map((cell) => String(cell.value)));
|
|
182
220
|
|
|
183
221
|
async function trigger_change(): Promise<void> {
|
|
222
|
+
// shouldnt trigger if data changed due to search
|
|
223
|
+
if (current_search_query) return;
|
|
184
224
|
const current_headers = _headers.map((h) => h.value);
|
|
185
225
|
const current_data = data.map((row) =>
|
|
186
226
|
row.map((cell) => String(cell.value))
|
|
@@ -312,8 +352,10 @@
|
|
|
312
352
|
editing = false;
|
|
313
353
|
} else {
|
|
314
354
|
selected_cells = [next_coords];
|
|
315
|
-
|
|
316
|
-
|
|
355
|
+
if (editable) {
|
|
356
|
+
editing = next_coords;
|
|
357
|
+
clear_on_focus = false;
|
|
358
|
+
}
|
|
317
359
|
}
|
|
318
360
|
selected = next_coords;
|
|
319
361
|
} else if (
|
|
@@ -333,27 +375,26 @@
|
|
|
333
375
|
editing = false;
|
|
334
376
|
break;
|
|
335
377
|
case "Enter":
|
|
336
|
-
if (!editable) break;
|
|
337
378
|
event.preventDefault();
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
await tick();
|
|
342
|
-
|
|
343
|
-
selected = [i + 1, j];
|
|
344
|
-
} else {
|
|
345
|
-
if (dequal(editing, [i, j])) {
|
|
346
|
-
const cell_id = data[i][j].id;
|
|
347
|
-
const input_el = els[cell_id].input;
|
|
348
|
-
if (input_el) {
|
|
349
|
-
data[i][j].value = input_el.value;
|
|
350
|
-
}
|
|
351
|
-
editing = false;
|
|
379
|
+
if (editable) {
|
|
380
|
+
if (event.shiftKey) {
|
|
381
|
+
add_row(i);
|
|
352
382
|
await tick();
|
|
353
|
-
selected = [i, j];
|
|
383
|
+
selected = [i + 1, j];
|
|
354
384
|
} else {
|
|
355
|
-
editing
|
|
356
|
-
|
|
385
|
+
if (dequal(editing, [i, j])) {
|
|
386
|
+
const cell_id = data[i][j].id;
|
|
387
|
+
const input_el = els[cell_id].input;
|
|
388
|
+
if (input_el) {
|
|
389
|
+
data[i][j].value = input_el.value;
|
|
390
|
+
}
|
|
391
|
+
editing = false;
|
|
392
|
+
await tick();
|
|
393
|
+
selected = [i, j];
|
|
394
|
+
} else {
|
|
395
|
+
editing = [i, j];
|
|
396
|
+
clear_on_focus = false;
|
|
397
|
+
}
|
|
357
398
|
}
|
|
358
399
|
}
|
|
359
400
|
break;
|
|
@@ -390,15 +431,16 @@
|
|
|
390
431
|
let sort_direction: SortDirection | undefined;
|
|
391
432
|
let sort_by: number | undefined;
|
|
392
433
|
|
|
393
|
-
function handle_sort(col: number): void {
|
|
434
|
+
function handle_sort(col: number, direction: SortDirection): void {
|
|
394
435
|
if (typeof sort_by !== "number" || sort_by !== col) {
|
|
395
|
-
sort_direction =
|
|
436
|
+
sort_direction = direction;
|
|
396
437
|
sort_by = col;
|
|
397
|
-
} else {
|
|
398
|
-
if (sort_direction ===
|
|
399
|
-
sort_direction =
|
|
400
|
-
|
|
401
|
-
|
|
438
|
+
} else if (sort_by === col) {
|
|
439
|
+
if (sort_direction === direction) {
|
|
440
|
+
sort_direction = undefined;
|
|
441
|
+
sort_by = undefined;
|
|
442
|
+
} else {
|
|
443
|
+
sort_direction = direction;
|
|
402
444
|
}
|
|
403
445
|
}
|
|
404
446
|
}
|
|
@@ -501,16 +543,25 @@
|
|
|
501
543
|
let table: HTMLTableElement;
|
|
502
544
|
|
|
503
545
|
function set_cell_widths(): void {
|
|
504
|
-
const widths = cells.map((el
|
|
505
|
-
return el?.clientWidth || 0;
|
|
506
|
-
});
|
|
546
|
+
const widths = cells.map((el) => el?.clientWidth || 0);
|
|
507
547
|
if (widths.length === 0) return;
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
`${widths[i] - scrollbar_width / widths.length}px`
|
|
512
|
-
);
|
|
548
|
+
|
|
549
|
+
if (show_row_numbers) {
|
|
550
|
+
parent.style.setProperty(`--cell-width-row-number`, `${widths[0]}px`);
|
|
513
551
|
}
|
|
552
|
+
const data_cells = show_row_numbers ? widths.slice(1) : widths;
|
|
553
|
+
data_cells.forEach((width, i) => {
|
|
554
|
+
if (!column_widths[i]) {
|
|
555
|
+
parent.style.setProperty(
|
|
556
|
+
`--cell-width-${i}`,
|
|
557
|
+
`${width - scrollbar_width / data_cells.length}px`
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function get_cell_width(index: number): string {
|
|
564
|
+
return column_widths[index] || `var(--cell-width-${index})`;
|
|
514
565
|
}
|
|
515
566
|
|
|
516
567
|
let table_height: number =
|
|
@@ -525,39 +576,14 @@
|
|
|
525
576
|
dir?: SortDirection
|
|
526
577
|
): void {
|
|
527
578
|
let id = null;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
id = data[selected[0]][selected[1]].id;
|
|
579
|
+
if (selected && selected[0] in _data && selected[1] in _data[selected[0]]) {
|
|
580
|
+
id = _data[selected[0]][selected[1]].id;
|
|
531
581
|
}
|
|
532
582
|
if (typeof col !== "number" || !dir) {
|
|
533
583
|
return;
|
|
534
584
|
}
|
|
535
|
-
const indices = [...Array(_data.length).keys()];
|
|
536
|
-
|
|
537
|
-
if (dir === "asc") {
|
|
538
|
-
indices.sort((i, j) =>
|
|
539
|
-
_data[i][col].value < _data[j][col].value ? -1 : 1
|
|
540
|
-
);
|
|
541
|
-
} else if (dir === "des") {
|
|
542
|
-
indices.sort((i, j) =>
|
|
543
|
-
_data[i][col].value > _data[j][col].value ? -1 : 1
|
|
544
|
-
);
|
|
545
|
-
} else {
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// sort all the data and metadata based on the values in the data
|
|
550
|
-
const temp_data = [..._data];
|
|
551
|
-
const temp_display_value = _display_value ? [..._display_value] : null;
|
|
552
|
-
const temp_styling = _styling ? [..._styling] : null;
|
|
553
|
-
indices.forEach((originalIndex, sortedIndex) => {
|
|
554
|
-
_data[sortedIndex] = temp_data[originalIndex];
|
|
555
|
-
if (_display_value && temp_display_value)
|
|
556
|
-
_display_value[sortedIndex] = temp_display_value[originalIndex];
|
|
557
|
-
if (_styling && temp_styling)
|
|
558
|
-
_styling[sortedIndex] = temp_styling[originalIndex];
|
|
559
|
-
});
|
|
560
585
|
|
|
586
|
+
sort_table_data(_data, _display_value, _styling, col, dir);
|
|
561
587
|
data = data;
|
|
562
588
|
|
|
563
589
|
if (id) {
|
|
@@ -604,8 +630,15 @@
|
|
|
604
630
|
row: number,
|
|
605
631
|
col: number
|
|
606
632
|
): void {
|
|
633
|
+
if (event.target instanceof HTMLAnchorElement) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
607
637
|
event.preventDefault();
|
|
608
638
|
event.stopPropagation();
|
|
639
|
+
|
|
640
|
+
if (show_row_numbers && col === -1) return;
|
|
641
|
+
|
|
609
642
|
clear_on_focus = false;
|
|
610
643
|
active_cell_menu = null;
|
|
611
644
|
active_header_menu = null;
|
|
@@ -613,19 +646,22 @@
|
|
|
613
646
|
header_edit = false;
|
|
614
647
|
|
|
615
648
|
selected_cells = handle_selection([row, col], selected_cells, event);
|
|
649
|
+
parent.focus();
|
|
616
650
|
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
input_el
|
|
623
|
-
|
|
624
|
-
input_el.
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
651
|
+
if (editable) {
|
|
652
|
+
if (selected_cells.length === 1) {
|
|
653
|
+
editing = [row, col];
|
|
654
|
+
tick().then(() => {
|
|
655
|
+
const input_el = els[data[row][col].id].input;
|
|
656
|
+
if (input_el) {
|
|
657
|
+
input_el.focus();
|
|
658
|
+
input_el.selectionStart = input_el.selectionEnd =
|
|
659
|
+
input_el.value.length;
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
} else {
|
|
663
|
+
editing = false;
|
|
664
|
+
}
|
|
629
665
|
}
|
|
630
666
|
|
|
631
667
|
toggle_cell_button(row, col);
|
|
@@ -671,6 +707,9 @@
|
|
|
671
707
|
function handle_resize(): void {
|
|
672
708
|
active_cell_menu = null;
|
|
673
709
|
active_header_menu = null;
|
|
710
|
+
selected_cells = [];
|
|
711
|
+
selected = false;
|
|
712
|
+
editing = false;
|
|
674
713
|
set_cell_widths();
|
|
675
714
|
}
|
|
676
715
|
|
|
@@ -711,7 +750,11 @@
|
|
|
711
750
|
}
|
|
712
751
|
|
|
713
752
|
async function handle_copy(): Promise<void> {
|
|
714
|
-
await copy_table_data(data,
|
|
753
|
+
await copy_table_data(data, selected_cells);
|
|
754
|
+
copy_flash = true;
|
|
755
|
+
setTimeout(() => {
|
|
756
|
+
copy_flash = false;
|
|
757
|
+
}, 800);
|
|
715
758
|
}
|
|
716
759
|
|
|
717
760
|
function toggle_header_menu(event: MouseEvent, col: number): void {
|
|
@@ -766,6 +809,94 @@
|
|
|
766
809
|
active_cell_menu = null;
|
|
767
810
|
active_header_menu = null;
|
|
768
811
|
}
|
|
812
|
+
|
|
813
|
+
let row_order: number[] = [];
|
|
814
|
+
|
|
815
|
+
$: {
|
|
816
|
+
if (
|
|
817
|
+
typeof sort_by === "number" &&
|
|
818
|
+
sort_direction &&
|
|
819
|
+
sort_by >= 0 &&
|
|
820
|
+
sort_by < data[0].length
|
|
821
|
+
) {
|
|
822
|
+
const indices = [...Array(data.length)].map((_, i) => i);
|
|
823
|
+
const sort_index = sort_by as number;
|
|
824
|
+
indices.sort((a, b) => {
|
|
825
|
+
const row_a = data[a];
|
|
826
|
+
const row_b = data[b];
|
|
827
|
+
if (
|
|
828
|
+
!row_a ||
|
|
829
|
+
!row_b ||
|
|
830
|
+
sort_index >= row_a.length ||
|
|
831
|
+
sort_index >= row_b.length
|
|
832
|
+
)
|
|
833
|
+
return 0;
|
|
834
|
+
const val_a = row_a[sort_index].value;
|
|
835
|
+
const val_b = row_b[sort_index].value;
|
|
836
|
+
const comp = val_a < val_b ? -1 : val_a > val_b ? 1 : 0;
|
|
837
|
+
return sort_direction === "asc" ? comp : -comp;
|
|
838
|
+
});
|
|
839
|
+
row_order = indices;
|
|
840
|
+
} else {
|
|
841
|
+
row_order = [...Array(data.length)].map((_, i) => i);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function handle_select_column(col: number): void {
|
|
846
|
+
selected_cells = select_column(data, col);
|
|
847
|
+
selected = selected_cells[0];
|
|
848
|
+
editing = false;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function handle_select_row(row: number): void {
|
|
852
|
+
selected_cells = select_row(data, row);
|
|
853
|
+
selected = selected_cells[0];
|
|
854
|
+
editing = false;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
let coords: CellCoordinate;
|
|
858
|
+
$: if (selected !== false) coords = selected;
|
|
859
|
+
|
|
860
|
+
$: if (selected !== false) {
|
|
861
|
+
const positions = calculate_selection_positions(
|
|
862
|
+
selected,
|
|
863
|
+
data,
|
|
864
|
+
els,
|
|
865
|
+
parent,
|
|
866
|
+
table
|
|
867
|
+
);
|
|
868
|
+
document.documentElement.style.setProperty(
|
|
869
|
+
"--selected-col-pos",
|
|
870
|
+
positions.col_pos
|
|
871
|
+
);
|
|
872
|
+
if (positions.row_pos) {
|
|
873
|
+
document.documentElement.style.setProperty(
|
|
874
|
+
"--selected-row-pos",
|
|
875
|
+
positions.row_pos
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
let current_search_query: string | null = null;
|
|
881
|
+
|
|
882
|
+
function handle_search(search_query: string | null): void {
|
|
883
|
+
current_search_query = search_query;
|
|
884
|
+
dispatch("search", search_query);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function commit_filter(): void {
|
|
888
|
+
if (current_search_query && show_search === "filter") {
|
|
889
|
+
dispatch("change", {
|
|
890
|
+
data: data.map((row) => row.map((cell) => cell.value)),
|
|
891
|
+
headers: _headers.map((h) => h.value),
|
|
892
|
+
metadata: null
|
|
893
|
+
});
|
|
894
|
+
if (!value_is_output) {
|
|
895
|
+
dispatch("input");
|
|
896
|
+
}
|
|
897
|
+
current_search_query = null;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
769
900
|
</script>
|
|
770
901
|
|
|
771
902
|
<svelte:window on:resize={() => set_cell_widths()} />
|
|
@@ -783,6 +914,10 @@
|
|
|
783
914
|
on:click={toggle_fullscreen}
|
|
784
915
|
on_copy={handle_copy}
|
|
785
916
|
{show_copy_button}
|
|
917
|
+
{show_search}
|
|
918
|
+
on:search={(e) => handle_search(e.detail)}
|
|
919
|
+
on_commit_filter={commit_filter}
|
|
920
|
+
{current_search_query}
|
|
786
921
|
/>
|
|
787
922
|
</div>
|
|
788
923
|
<div
|
|
@@ -790,11 +925,28 @@
|
|
|
790
925
|
class="table-wrap"
|
|
791
926
|
class:dragging
|
|
792
927
|
class:no-wrap={!wrap}
|
|
793
|
-
style="height:{table_height}px"
|
|
928
|
+
style="height:{table_height}px;"
|
|
929
|
+
class:menu-open={active_cell_menu || active_header_menu}
|
|
794
930
|
on:keydown={(e) => handle_keydown(e)}
|
|
795
931
|
role="grid"
|
|
796
932
|
tabindex="0"
|
|
797
933
|
>
|
|
934
|
+
{#if selected !== false && selected_cells.length === 1}
|
|
935
|
+
<button
|
|
936
|
+
class="selection-button selection-button-column"
|
|
937
|
+
on:click|stopPropagation={() => handle_select_column(coords[1])}
|
|
938
|
+
aria-label="Select column"
|
|
939
|
+
>
|
|
940
|
+
⋮
|
|
941
|
+
</button>
|
|
942
|
+
<button
|
|
943
|
+
class="selection-button selection-button-row"
|
|
944
|
+
on:click|stopPropagation={() => handle_select_row(coords[0])}
|
|
945
|
+
aria-label="Select row"
|
|
946
|
+
>
|
|
947
|
+
⋮
|
|
948
|
+
</button>
|
|
949
|
+
{/if}
|
|
798
950
|
<table
|
|
799
951
|
bind:contentRect={t_rect}
|
|
800
952
|
bind:this={table}
|
|
@@ -806,40 +958,59 @@
|
|
|
806
958
|
<thead>
|
|
807
959
|
<tr>
|
|
808
960
|
{#if show_row_numbers}
|
|
809
|
-
<th
|
|
961
|
+
<th
|
|
962
|
+
class="row-number-header frozen-column always-frozen"
|
|
963
|
+
style="left: 0;"
|
|
964
|
+
>
|
|
965
|
+
<div class="cell-wrap">
|
|
966
|
+
<div class="header-content">
|
|
967
|
+
<div class="header-text"></div>
|
|
968
|
+
</div>
|
|
969
|
+
</div>
|
|
970
|
+
</th>
|
|
810
971
|
{/if}
|
|
811
972
|
{#each _headers as { value, id }, i (id)}
|
|
812
973
|
<th
|
|
974
|
+
class:frozen-column={i < actual_pinned_columns}
|
|
975
|
+
class:last-frozen={show_row_numbers
|
|
976
|
+
? i === actual_pinned_columns - 1
|
|
977
|
+
: i === actual_pinned_columns - 1}
|
|
813
978
|
class:editing={header_edit === i}
|
|
814
979
|
aria-sort={get_sort_status(value, sort_by, sort_direction)}
|
|
815
|
-
style:
|
|
980
|
+
style="width: {column_widths.length
|
|
981
|
+
? column_widths[i]
|
|
982
|
+
: undefined}; left: {i < actual_pinned_columns
|
|
983
|
+
? i === 0
|
|
984
|
+
? show_row_numbers
|
|
985
|
+
? 'var(--cell-width-row-number)'
|
|
986
|
+
: '0'
|
|
987
|
+
: `calc(${show_row_numbers ? 'var(--cell-width-row-number) + ' : ''}${Array(
|
|
988
|
+
i
|
|
989
|
+
)
|
|
990
|
+
.fill(0)
|
|
991
|
+
.map((_, idx) => `var(--cell-width-${idx})`)
|
|
992
|
+
.join(' + ')})`
|
|
993
|
+
: 'auto'};"
|
|
816
994
|
>
|
|
817
995
|
<div class="cell-wrap">
|
|
818
|
-
<
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
height="1em"
|
|
837
|
-
viewBox="0 0 9 7"
|
|
838
|
-
fill="none"
|
|
839
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
840
|
-
>
|
|
841
|
-
<path d="M4.49999 0L8.3971 6.75H0.602875L4.49999 0Z" />
|
|
842
|
-
</svg>
|
|
996
|
+
<div class="header-content">
|
|
997
|
+
<EditableCell
|
|
998
|
+
{value}
|
|
999
|
+
{latex_delimiters}
|
|
1000
|
+
{line_breaks}
|
|
1001
|
+
header
|
|
1002
|
+
edit={false}
|
|
1003
|
+
el={null}
|
|
1004
|
+
{root}
|
|
1005
|
+
{editable}
|
|
1006
|
+
/>
|
|
1007
|
+
<div class="sort-buttons">
|
|
1008
|
+
<SortIcon
|
|
1009
|
+
direction={sort_by === i ? sort_direction : null}
|
|
1010
|
+
on:sort={({ detail }) => handle_sort(i, detail)}
|
|
1011
|
+
{i18n}
|
|
1012
|
+
/>
|
|
1013
|
+
</div>
|
|
843
1014
|
</div>
|
|
844
1015
|
</div>
|
|
845
1016
|
</th>
|
|
@@ -878,9 +1049,12 @@
|
|
|
878
1049
|
on:load={({ detail }) =>
|
|
879
1050
|
handle_file_upload(
|
|
880
1051
|
detail.data,
|
|
881
|
-
col_count,
|
|
882
1052
|
(head) => {
|
|
883
|
-
_headers = make_headers(
|
|
1053
|
+
_headers = make_headers(
|
|
1054
|
+
head.map((h) => h ?? ""),
|
|
1055
|
+
col_count,
|
|
1056
|
+
els
|
|
1057
|
+
);
|
|
884
1058
|
return _headers;
|
|
885
1059
|
},
|
|
886
1060
|
(vals) => {
|
|
@@ -896,19 +1070,44 @@
|
|
|
896
1070
|
bind:actual_height={table_height}
|
|
897
1071
|
bind:table_scrollbar_width={scrollbar_width}
|
|
898
1072
|
selected={selected_index}
|
|
1073
|
+
disable_scroll={active_cell_menu !== null ||
|
|
1074
|
+
active_header_menu !== null}
|
|
899
1075
|
>
|
|
900
1076
|
{#if label && label.length !== 0}
|
|
901
1077
|
<caption class="sr-only">{label}</caption>
|
|
902
1078
|
{/if}
|
|
903
1079
|
<tr slot="thead">
|
|
904
1080
|
{#if show_row_numbers}
|
|
905
|
-
<th
|
|
1081
|
+
<th
|
|
1082
|
+
class="row-number-header frozen-column always-frozen"
|
|
1083
|
+
style="left: 0;"
|
|
1084
|
+
>
|
|
1085
|
+
<div class="cell-wrap">
|
|
1086
|
+
<div class="header-content">
|
|
1087
|
+
<div class="header-text"></div>
|
|
1088
|
+
</div>
|
|
1089
|
+
</div>
|
|
1090
|
+
</th>
|
|
906
1091
|
{/if}
|
|
907
1092
|
{#each _headers as { value, id }, i (id)}
|
|
908
1093
|
<th
|
|
1094
|
+
class:frozen-column={i < actual_pinned_columns}
|
|
1095
|
+
class:last-frozen={i === actual_pinned_columns - 1}
|
|
909
1096
|
class:focus={header_edit === i || selected_header === i}
|
|
910
1097
|
aria-sort={get_sort_status(value, sort_by, sort_direction)}
|
|
911
|
-
style="width:
|
|
1098
|
+
style="width: {get_cell_width(i)}; left: {i <
|
|
1099
|
+
actual_pinned_columns
|
|
1100
|
+
? i === 0
|
|
1101
|
+
? show_row_numbers
|
|
1102
|
+
? 'var(--cell-width-row-number)'
|
|
1103
|
+
: '0'
|
|
1104
|
+
: `calc(${show_row_numbers ? 'var(--cell-width-row-number) + ' : ''}${Array(
|
|
1105
|
+
i
|
|
1106
|
+
)
|
|
1107
|
+
.fill(0)
|
|
1108
|
+
.map((_, idx) => `var(--cell-width-${idx})`)
|
|
1109
|
+
.join(' + ')})`
|
|
1110
|
+
: 'auto'};"
|
|
912
1111
|
on:click={() => {
|
|
913
1112
|
toggle_header_button(i);
|
|
914
1113
|
}}
|
|
@@ -928,28 +1127,14 @@
|
|
|
928
1127
|
{root}
|
|
929
1128
|
{editable}
|
|
930
1129
|
/>
|
|
931
|
-
<
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
handle_sort(i);
|
|
939
|
-
}}
|
|
940
|
-
>
|
|
941
|
-
<svg
|
|
942
|
-
width="1em"
|
|
943
|
-
height="1em"
|
|
944
|
-
viewBox="0 0 9 7"
|
|
945
|
-
fill="none"
|
|
946
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
947
|
-
>
|
|
948
|
-
<path d="M4.49999 0L8.3971 6.75H0.602875L4.49999 0Z" />
|
|
949
|
-
</svg>
|
|
950
|
-
</button>
|
|
1130
|
+
<div class="sort-buttons">
|
|
1131
|
+
<SortIcon
|
|
1132
|
+
direction={sort_by === i ? sort_direction : null}
|
|
1133
|
+
on:sort={({ detail }) => handle_sort(i, detail)}
|
|
1134
|
+
{i18n}
|
|
1135
|
+
/>
|
|
1136
|
+
</div>
|
|
951
1137
|
</div>
|
|
952
|
-
|
|
953
1138
|
{#if editable}
|
|
954
1139
|
<button
|
|
955
1140
|
class="cell-menu-button"
|
|
@@ -962,14 +1147,22 @@
|
|
|
962
1147
|
</th>
|
|
963
1148
|
{/each}
|
|
964
1149
|
</tr>
|
|
965
|
-
|
|
966
1150
|
<tr slot="tbody" let:item let:index class:row_odd={index % 2 === 0}>
|
|
967
1151
|
{#if show_row_numbers}
|
|
968
|
-
<td
|
|
1152
|
+
<td
|
|
1153
|
+
class="row-number frozen-column always-frozen"
|
|
1154
|
+
style="left: 0;"
|
|
1155
|
+
tabindex="-1"
|
|
1156
|
+
>
|
|
1157
|
+
{index + 1}
|
|
1158
|
+
</td>
|
|
969
1159
|
{/if}
|
|
970
1160
|
{#each item as { value, id }, j (id)}
|
|
971
1161
|
<td
|
|
972
|
-
|
|
1162
|
+
class:frozen-column={j < actual_pinned_columns}
|
|
1163
|
+
class:last-frozen={j === actual_pinned_columns - 1}
|
|
1164
|
+
tabindex={show_row_numbers && j === 0 ? -1 : 0}
|
|
1165
|
+
bind:this={els[id].cell}
|
|
973
1166
|
on:touchstart={(event) => {
|
|
974
1167
|
const touch = event.touches[0];
|
|
975
1168
|
const mouseEvent = new MouseEvent("click", {
|
|
@@ -986,8 +1179,21 @@
|
|
|
986
1179
|
event.stopPropagation();
|
|
987
1180
|
}}
|
|
988
1181
|
on:click={(event) => handle_cell_click(event, index, j)}
|
|
989
|
-
style
|
|
990
|
-
|
|
1182
|
+
style="width: {get_cell_width(j)}; left: {j <
|
|
1183
|
+
actual_pinned_columns
|
|
1184
|
+
? j === 0
|
|
1185
|
+
? show_row_numbers
|
|
1186
|
+
? 'var(--cell-width-row-number)'
|
|
1187
|
+
: '0'
|
|
1188
|
+
: `calc(${show_row_numbers ? 'var(--cell-width-row-number) + ' : ''}${Array(
|
|
1189
|
+
j
|
|
1190
|
+
)
|
|
1191
|
+
.fill(0)
|
|
1192
|
+
.map((_, idx) => `var(--cell-width-${idx})`)
|
|
1193
|
+
.join(' + ')})`
|
|
1194
|
+
: 'auto'}; {styling?.[index]?.[j] || ''}"
|
|
1195
|
+
class:flash={copy_flash &&
|
|
1196
|
+
is_cell_selected([index, j], selected_cells)}
|
|
991
1197
|
class={is_cell_selected([index, j], selected_cells)}
|
|
992
1198
|
class:menu-active={active_cell_menu &&
|
|
993
1199
|
active_cell_menu.row === index &&
|
|
@@ -1077,6 +1283,14 @@
|
|
|
1077
1283
|
{/if}
|
|
1078
1284
|
|
|
1079
1285
|
<style>
|
|
1286
|
+
.label p {
|
|
1287
|
+
position: relative;
|
|
1288
|
+
z-index: var(--layer-4);
|
|
1289
|
+
margin-bottom: var(--size-2);
|
|
1290
|
+
color: var(--block-label-text-color);
|
|
1291
|
+
font-size: var(--block-label-text-size);
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1080
1294
|
.table-container {
|
|
1081
1295
|
display: flex;
|
|
1082
1296
|
flex-direction: column;
|
|
@@ -1088,6 +1302,9 @@
|
|
|
1088
1302
|
transition: 150ms;
|
|
1089
1303
|
border: 1px solid var(--border-color-primary);
|
|
1090
1304
|
border-radius: var(--table-radius);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.table-wrap.menu-open {
|
|
1091
1308
|
overflow: hidden;
|
|
1092
1309
|
}
|
|
1093
1310
|
|
|
@@ -1132,8 +1349,7 @@
|
|
|
1132
1349
|
thead {
|
|
1133
1350
|
position: sticky;
|
|
1134
1351
|
top: 0;
|
|
1135
|
-
|
|
1136
|
-
z-index: var(--layer-1);
|
|
1352
|
+
z-index: var(--layer-2);
|
|
1137
1353
|
box-shadow: var(--shadow-drop);
|
|
1138
1354
|
}
|
|
1139
1355
|
|
|
@@ -1189,32 +1405,10 @@
|
|
|
1189
1405
|
background: var(--table-even-background-fill);
|
|
1190
1406
|
}
|
|
1191
1407
|
|
|
1192
|
-
|
|
1193
|
-
fill: currentColor;
|
|
1194
|
-
font-size: 10px;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
.sort-button {
|
|
1408
|
+
.sort-buttons {
|
|
1198
1409
|
display: flex;
|
|
1199
|
-
flex: none;
|
|
1200
|
-
justify-content: center;
|
|
1201
1410
|
align-items: center;
|
|
1202
|
-
|
|
1203
|
-
cursor: pointer;
|
|
1204
|
-
padding: var(--size-2);
|
|
1205
|
-
color: var(--body-text-color-subdued);
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
.sort-button:hover {
|
|
1209
|
-
color: var(--body-text-color);
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
.des {
|
|
1213
|
-
transform: scaleY(-1);
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
.sort-button.sorted {
|
|
1217
|
-
color: var(--color-accent);
|
|
1411
|
+
flex-shrink: 0;
|
|
1218
1412
|
}
|
|
1219
1413
|
|
|
1220
1414
|
.editing {
|
|
@@ -1233,12 +1427,16 @@
|
|
|
1233
1427
|
.header-content {
|
|
1234
1428
|
display: flex;
|
|
1235
1429
|
align-items: center;
|
|
1430
|
+
justify-content: space-between;
|
|
1236
1431
|
overflow: hidden;
|
|
1237
1432
|
flex-grow: 1;
|
|
1238
1433
|
min-width: 0;
|
|
1239
1434
|
white-space: normal;
|
|
1240
1435
|
overflow-wrap: break-word;
|
|
1241
|
-
word-break:
|
|
1436
|
+
word-break: normal;
|
|
1437
|
+
height: 100%;
|
|
1438
|
+
padding: var(--size-1);
|
|
1439
|
+
gap: var(--size-1);
|
|
1242
1440
|
}
|
|
1243
1441
|
|
|
1244
1442
|
.row_odd {
|
|
@@ -1275,46 +1473,55 @@
|
|
|
1275
1473
|
|
|
1276
1474
|
.header-row {
|
|
1277
1475
|
display: flex;
|
|
1278
|
-
justify-content:
|
|
1476
|
+
justify-content: flex-end;
|
|
1279
1477
|
align-items: center;
|
|
1280
1478
|
gap: var(--size-2);
|
|
1281
|
-
height: var(--size-6);
|
|
1282
1479
|
min-height: var(--size-6);
|
|
1480
|
+
flex-wrap: nowrap;
|
|
1481
|
+
width: 100%;
|
|
1283
1482
|
}
|
|
1284
1483
|
|
|
1285
1484
|
.label {
|
|
1286
|
-
flex: 1;
|
|
1485
|
+
flex: 1 1 auto;
|
|
1486
|
+
margin-right: auto;
|
|
1287
1487
|
}
|
|
1288
1488
|
|
|
1289
1489
|
.label p {
|
|
1290
1490
|
margin: 0;
|
|
1291
1491
|
color: var(--block-label-text-color);
|
|
1292
1492
|
font-size: var(--block-label-text-size);
|
|
1493
|
+
line-height: var(--line-sm);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
.toolbar {
|
|
1497
|
+
flex: 0 0 auto;
|
|
1293
1498
|
}
|
|
1294
1499
|
|
|
1295
1500
|
.row-number,
|
|
1296
1501
|
.row-number-header {
|
|
1297
|
-
width: var(--size-7);
|
|
1298
|
-
min-width: var(--size-7);
|
|
1299
1502
|
text-align: center;
|
|
1300
1503
|
background: var(--table-even-background-fill);
|
|
1301
|
-
position: sticky;
|
|
1302
|
-
left: 0;
|
|
1303
1504
|
font-size: var(--input-text-size);
|
|
1304
1505
|
color: var(--body-text-color);
|
|
1305
|
-
padding: var(--size-1)
|
|
1506
|
+
padding: var(--size-1);
|
|
1507
|
+
min-width: var(--size-12);
|
|
1508
|
+
width: var(--size-12);
|
|
1306
1509
|
overflow: hidden;
|
|
1307
1510
|
text-overflow: ellipsis;
|
|
1308
1511
|
white-space: nowrap;
|
|
1309
1512
|
font-weight: var(--weight-semibold);
|
|
1310
1513
|
}
|
|
1311
1514
|
|
|
1312
|
-
.row-number-header {
|
|
1313
|
-
|
|
1515
|
+
.row-number-header .header-content {
|
|
1516
|
+
justify-content: space-between;
|
|
1517
|
+
padding: var(--size-1);
|
|
1518
|
+
height: var(--size-9);
|
|
1519
|
+
display: flex;
|
|
1520
|
+
align-items: center;
|
|
1314
1521
|
}
|
|
1315
1522
|
|
|
1316
|
-
.row-number {
|
|
1317
|
-
|
|
1523
|
+
.row-number-header :global(.sort-icons) {
|
|
1524
|
+
margin-right: 0;
|
|
1318
1525
|
}
|
|
1319
1526
|
|
|
1320
1527
|
:global(tbody > tr:nth-child(odd)) .row-number {
|
|
@@ -1411,4 +1618,68 @@
|
|
|
1411
1618
|
.cell-selected.no-top.no-bottom.no-left.no-right {
|
|
1412
1619
|
box-shadow: none;
|
|
1413
1620
|
}
|
|
1621
|
+
|
|
1622
|
+
.selection-button {
|
|
1623
|
+
position: absolute;
|
|
1624
|
+
display: flex;
|
|
1625
|
+
align-items: center;
|
|
1626
|
+
justify-content: center;
|
|
1627
|
+
background: var(--color-accent);
|
|
1628
|
+
color: white;
|
|
1629
|
+
border-radius: var(--radius-sm);
|
|
1630
|
+
z-index: var(--layer-4);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
.selection-button-column {
|
|
1634
|
+
width: var(--size-3);
|
|
1635
|
+
height: var(--size-5);
|
|
1636
|
+
top: -10px;
|
|
1637
|
+
left: var(--selected-col-pos);
|
|
1638
|
+
transform: rotate(90deg);
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
.selection-button-row {
|
|
1642
|
+
width: var(--size-3);
|
|
1643
|
+
height: var(--size-5);
|
|
1644
|
+
left: -7px;
|
|
1645
|
+
top: calc(var(--selected-row-pos) - var(--size-5) / 2);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
.table-wrap:not(:focus-within) .selection-button {
|
|
1649
|
+
opacity: 0;
|
|
1650
|
+
pointer-events: none;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
.flash.cell-selected {
|
|
1654
|
+
animation: flash-color 700ms ease-out;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
@keyframes flash-color {
|
|
1658
|
+
0%,
|
|
1659
|
+
30% {
|
|
1660
|
+
background: var(--color-accent-copied);
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
100% {
|
|
1664
|
+
background: transparent;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
.frozen-column {
|
|
1669
|
+
position: sticky;
|
|
1670
|
+
z-index: var(--layer-2);
|
|
1671
|
+
border-right: 1px solid var(--border-color-primary);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
tr:nth-child(odd) .frozen-column {
|
|
1675
|
+
background: var(--table-odd-background-fill);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
tr:nth-child(even) .frozen-column {
|
|
1679
|
+
background: var(--table-even-background-fill);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
.always-frozen {
|
|
1683
|
+
z-index: var(--layer-3);
|
|
1684
|
+
}
|
|
1414
1685
|
</style>
|