@gradio/dataframe 0.24.2 → 0.24.4
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 +33 -0
- package/Dataframe.test.ts +497 -43
- package/Example.svelte +19 -10
- package/dist/Example.svelte +19 -10
- package/dist/Example.svelte.d.ts +4 -18
- package/dist/shared/BooleanCell.svelte +1 -0
- package/dist/shared/CellMenuButton.svelte +8 -1
- package/dist/shared/CellMenuButton.svelte.d.ts +1 -0
- package/dist/shared/DataCell.svelte +26 -2
- package/dist/shared/DataCell.svelte.d.ts +4 -0
- package/dist/shared/EditableCell.svelte +6 -3
- package/dist/shared/Example.svelte +1 -1
- package/dist/shared/Example.svelte.d.ts +4 -18
- package/dist/shared/HeaderCell.svelte +17 -3
- package/dist/shared/HeaderCell.svelte.d.ts +3 -1
- package/dist/shared/Table.svelte +374 -140
- package/dist/shared/icons/SelectionButtons.svelte +1 -0
- package/dist/shared/tanstack/virtual.svelte.js +2 -2
- package/dist/standalone/stubs/Upload.svelte +12 -5
- package/dist/standalone/stubs/Upload.svelte.d.ts +8 -34
- package/package.json +8 -8
- package/shared/BooleanCell.svelte +1 -0
- package/shared/CellMenuButton.svelte +8 -1
- package/shared/DataCell.svelte +26 -2
- package/shared/EditableCell.svelte +6 -3
- package/shared/Example.svelte +1 -1
- package/shared/HeaderCell.svelte +17 -3
- package/shared/Table.svelte +374 -140
- package/shared/icons/SelectionButtons.svelte +1 -0
- package/shared/tanstack/virtual.svelte.ts +2 -2
- package/standalone/stubs/Upload.svelte +12 -5
package/shared/Table.svelte
CHANGED
|
@@ -255,6 +255,13 @@
|
|
|
255
255
|
let editing: CellCoordinate | false = $state(false);
|
|
256
256
|
let header_edit: number | false = $state(false);
|
|
257
257
|
let selected_header: number | false = $state(false);
|
|
258
|
+
let keyboard_header: number | false = $derived(
|
|
259
|
+
selected_header !== false &&
|
|
260
|
+
selected_header >= 0 &&
|
|
261
|
+
selected_header < resolved_headers.length
|
|
262
|
+
? selected_header
|
|
263
|
+
: false
|
|
264
|
+
);
|
|
258
265
|
let active_cell_menu: {
|
|
259
266
|
row: number;
|
|
260
267
|
col: number;
|
|
@@ -270,10 +277,140 @@
|
|
|
270
277
|
|
|
271
278
|
let parent: HTMLDivElement;
|
|
272
279
|
|
|
280
|
+
let keyboard_active = $derived.by((): CellCoordinate | false => {
|
|
281
|
+
if (selected) {
|
|
282
|
+
const selected_row = selected[0];
|
|
283
|
+
const is_visible = rows.some(
|
|
284
|
+
(row) => row.original._index === selected_row
|
|
285
|
+
);
|
|
286
|
+
if (is_visible) return selected;
|
|
287
|
+
}
|
|
288
|
+
if (rows.length > 0 && resolved_headers.length > 0) {
|
|
289
|
+
return [rows[0].original._index, 0];
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
function visible_row_position(row_index: number): number {
|
|
295
|
+
return rows.findIndex((row) => row.original._index === row_index);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function is_active_cell(row: number, col: number): boolean {
|
|
299
|
+
return !!(
|
|
300
|
+
keyboard_active &&
|
|
301
|
+
keyboard_active[0] === row &&
|
|
302
|
+
keyboard_active[1] === col &&
|
|
303
|
+
keyboard_header === false
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function focus_cell(coord: CellCoordinate): Promise<void> {
|
|
308
|
+
await tick();
|
|
309
|
+
parent
|
|
310
|
+
?.querySelector<HTMLElement>(
|
|
311
|
+
`[data-testid="cell-${coord[0]}-${coord[1]}"]`
|
|
312
|
+
)
|
|
313
|
+
?.focus();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function focus_header(col: number): Promise<void> {
|
|
317
|
+
await tick();
|
|
318
|
+
parent
|
|
319
|
+
?.querySelector<HTMLElement>(`[data-testid="header-${col}"]`)
|
|
320
|
+
?.focus();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function set_active_cell(coord: CellCoordinate, move_focus = true): void {
|
|
324
|
+
selected = coord;
|
|
325
|
+
selected_cells = [coord];
|
|
326
|
+
selected_header = false;
|
|
327
|
+
header_edit = false;
|
|
328
|
+
const row_position = visible_row_position(coord[0]);
|
|
329
|
+
if (row_position >= 0) {
|
|
330
|
+
virtualizer.instance.scrollToIndex(row_position, { align: "auto" });
|
|
331
|
+
}
|
|
332
|
+
if (move_focus) focus_cell(coord);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function handle_cell_focus(row: number, col: number): void {
|
|
336
|
+
if (editing) return;
|
|
337
|
+
if (selected && selected[0] === row && selected[1] === col) return;
|
|
338
|
+
set_active_cell([row, col], false);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function handle_header_focus(col: number): void {
|
|
342
|
+
if (header_edit !== false) return;
|
|
343
|
+
selected = false;
|
|
344
|
+
selected_cells = [];
|
|
345
|
+
selected_header = col;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function dispatch_select(row: number, col: number): void {
|
|
349
|
+
onselect?.({
|
|
350
|
+
index: [row, col],
|
|
351
|
+
value: values?.[row]?.[col],
|
|
352
|
+
row_value: values?.[row] ?? [],
|
|
353
|
+
col_value: values?.map((value_row) => value_row[col]) ?? []
|
|
354
|
+
} as any);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function adjacent_visible_cell(
|
|
358
|
+
coord: CellCoordinate,
|
|
359
|
+
backwards: boolean
|
|
360
|
+
): CellCoordinate | false {
|
|
361
|
+
const [row, col] = coord;
|
|
362
|
+
const row_position = visible_row_position(row);
|
|
363
|
+
if (row_position === -1) return false;
|
|
364
|
+
|
|
365
|
+
if (backwards) {
|
|
366
|
+
if (col > 0) return [row, col - 1];
|
|
367
|
+
if (row_position > 0) {
|
|
368
|
+
return [
|
|
369
|
+
rows[row_position - 1].original._index,
|
|
370
|
+
resolved_headers.length - 1
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (col < resolved_headers.length - 1) return [row, col + 1];
|
|
377
|
+
if (row_position < rows.length - 1) {
|
|
378
|
+
return [rows[row_position + 1].original._index, 0];
|
|
379
|
+
}
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function focus_outside_grid(backwards: boolean): void {
|
|
384
|
+
tick().then(() => {
|
|
385
|
+
const candidates = Array.from(
|
|
386
|
+
document.querySelectorAll<HTMLElement>(
|
|
387
|
+
'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
388
|
+
)
|
|
389
|
+
).filter(
|
|
390
|
+
(element) =>
|
|
391
|
+
!parent.contains(element) && element.getClientRects().length > 0
|
|
392
|
+
);
|
|
393
|
+
const position = backwards
|
|
394
|
+
? Node.DOCUMENT_POSITION_PRECEDING
|
|
395
|
+
: Node.DOCUMENT_POSITION_FOLLOWING;
|
|
396
|
+
const ordered = backwards ? candidates.reverse() : candidates;
|
|
397
|
+
ordered
|
|
398
|
+
.find((element) => parent.compareDocumentPosition(element) & position)
|
|
399
|
+
?.focus();
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
273
403
|
function get_dtype(col: number): Datatype {
|
|
274
404
|
return Array.isArray(datatype) ? (datatype[col] ?? "str") : datatype;
|
|
275
405
|
}
|
|
276
406
|
|
|
407
|
+
function is_static_column(col: number): boolean {
|
|
408
|
+
return (
|
|
409
|
+
static_columns.includes(col) ||
|
|
410
|
+
static_columns.includes(resolved_headers[col])
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
277
414
|
type SizingEntry = { val: string; col_idx: number; dtype: Datatype };
|
|
278
415
|
|
|
279
416
|
// heading multipliers for markdown/html block elements that render
|
|
@@ -418,16 +555,8 @@
|
|
|
418
555
|
// click selects, does NOT enter edit mode (double-click or typing does)
|
|
419
556
|
editing = false;
|
|
420
557
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
value: values?.[row]?.[col],
|
|
424
|
-
row_value: values?.[row] ?? [],
|
|
425
|
-
col_value: values?.map((r) => r[col]) ?? []
|
|
426
|
-
} as any);
|
|
427
|
-
|
|
428
|
-
if (!col_is_static) {
|
|
429
|
-
tick().then(() => parent?.focus());
|
|
430
|
-
}
|
|
558
|
+
dispatch_select(row, col);
|
|
559
|
+
focus_cell(coord);
|
|
431
560
|
}
|
|
432
561
|
|
|
433
562
|
function handle_cell_dblclick(
|
|
@@ -475,7 +604,6 @@
|
|
|
475
604
|
if (event.target instanceof HTMLAnchorElement) return;
|
|
476
605
|
event.preventDefault();
|
|
477
606
|
event.stopPropagation();
|
|
478
|
-
if (!editable) return;
|
|
479
607
|
|
|
480
608
|
editing = false;
|
|
481
609
|
selected = false;
|
|
@@ -483,14 +611,19 @@
|
|
|
483
611
|
active_cell_menu = null;
|
|
484
612
|
active_header_menu = null;
|
|
485
613
|
selected_header = col;
|
|
486
|
-
header_edit = editable ? col : false;
|
|
487
|
-
|
|
614
|
+
header_edit = editable && !is_static_column(col) ? col : false;
|
|
615
|
+
if (header_edit === false) focus_header(col);
|
|
488
616
|
}
|
|
489
617
|
|
|
490
|
-
function end_header_edit(
|
|
491
|
-
if (["Escape", "Enter", "Tab"].includes(key)) {
|
|
618
|
+
function end_header_edit(event: KeyboardEvent): void {
|
|
619
|
+
if (["Escape", "Enter", "Tab"].includes(event.key)) {
|
|
620
|
+
event.preventDefault();
|
|
492
621
|
header_edit = false;
|
|
493
|
-
|
|
622
|
+
if (event.key === "Tab") {
|
|
623
|
+
focus_outside_grid(event.shiftKey);
|
|
624
|
+
} else if (selected_header !== false) {
|
|
625
|
+
focus_header(selected_header);
|
|
626
|
+
}
|
|
494
627
|
}
|
|
495
628
|
}
|
|
496
629
|
|
|
@@ -565,8 +698,7 @@
|
|
|
565
698
|
}
|
|
566
699
|
values = new_values;
|
|
567
700
|
push_change(new_values);
|
|
568
|
-
|
|
569
|
-
parent?.focus();
|
|
701
|
+
set_active_cell([index ?? new_values.length - 1, 0]);
|
|
570
702
|
}
|
|
571
703
|
|
|
572
704
|
function add_col(index?: number): void {
|
|
@@ -583,20 +715,33 @@
|
|
|
583
715
|
values = new_values;
|
|
584
716
|
headers = new_headers;
|
|
585
717
|
push_change(new_values, new_headers);
|
|
586
|
-
|
|
718
|
+
const new_col = index ?? new_headers.length - 1;
|
|
719
|
+
if (selected) {
|
|
720
|
+
set_active_cell([selected[0], new_col]);
|
|
721
|
+
} else {
|
|
722
|
+
selected_header = new_col;
|
|
723
|
+
header_edit = false;
|
|
724
|
+
focus_header(new_col);
|
|
725
|
+
}
|
|
587
726
|
}
|
|
588
727
|
|
|
589
728
|
function delete_row_at(index: number): void {
|
|
590
729
|
if (values.length <= 1) return;
|
|
730
|
+
const target_col = selected ? selected[1] : 0;
|
|
591
731
|
values = [...values.slice(0, index), ...values.slice(index + 1)];
|
|
592
732
|
push_change(values);
|
|
593
733
|
active_cell_menu = null;
|
|
594
734
|
active_header_menu = null;
|
|
735
|
+
set_active_cell([
|
|
736
|
+
Math.min(index, values.length - 1),
|
|
737
|
+
Math.min(target_col, resolved_headers.length - 1)
|
|
738
|
+
]);
|
|
595
739
|
}
|
|
596
740
|
|
|
597
741
|
function delete_col_at(index: number): void {
|
|
598
742
|
if (col_count[1] !== "dynamic") return;
|
|
599
743
|
if ((values[0]?.length ?? 0) <= 1) return;
|
|
744
|
+
const target_row = selected ? selected[0] : (rows[0]?.original._index ?? 0);
|
|
600
745
|
values = values.map((row) => [
|
|
601
746
|
...row.slice(0, index),
|
|
602
747
|
...row.slice(index + 1)
|
|
@@ -608,9 +753,8 @@
|
|
|
608
753
|
push_change(values, headers as string[]);
|
|
609
754
|
active_cell_menu = null;
|
|
610
755
|
active_header_menu = null;
|
|
611
|
-
selected = false;
|
|
612
|
-
selected_cells = [];
|
|
613
756
|
editing = false;
|
|
757
|
+
set_active_cell([target_row, Math.min(index, headers.length - 1)]);
|
|
614
758
|
}
|
|
615
759
|
|
|
616
760
|
function add_row_at(index: number, position: "above" | "below"): void {
|
|
@@ -682,137 +826,206 @@
|
|
|
682
826
|
}
|
|
683
827
|
|
|
684
828
|
function handle_keydown(e: KeyboardEvent): void {
|
|
685
|
-
if (
|
|
829
|
+
if (e.target instanceof HTMLAnchorElement) return;
|
|
830
|
+
if (!selected && keyboard_header === false) return;
|
|
686
831
|
|
|
687
|
-
const num_rows = rows.length;
|
|
688
832
|
const num_cols = resolved_headers.length;
|
|
689
833
|
|
|
690
|
-
if (
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
if (
|
|
694
|
-
editing &&
|
|
695
|
-
(e.key === "ArrowUp" ||
|
|
696
|
-
e.key === "ArrowDown" ||
|
|
697
|
-
e.key === "ArrowLeft" ||
|
|
698
|
-
e.key === "ArrowRight")
|
|
699
|
-
) {
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
|
|
834
|
+
if (keyboard_header !== false) {
|
|
835
|
+
if (header_edit !== false) return;
|
|
836
|
+
const col = keyboard_header;
|
|
703
837
|
switch (e.key) {
|
|
704
|
-
case "
|
|
838
|
+
case "ArrowDown": {
|
|
705
839
|
e.preventDefault();
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
selected_cells = [selected];
|
|
709
|
-
virtualizer.instance.scrollToIndex(row - 1, { align: "auto" });
|
|
710
|
-
}
|
|
711
|
-
break;
|
|
712
|
-
case "ArrowDown":
|
|
713
|
-
e.preventDefault();
|
|
714
|
-
if (row < num_rows - 1) {
|
|
715
|
-
selected = [row + 1, col];
|
|
716
|
-
selected_cells = [selected];
|
|
717
|
-
virtualizer.instance.scrollToIndex(row + 1, { align: "auto" });
|
|
718
|
-
}
|
|
840
|
+
const first_row = rows[0]?.original._index;
|
|
841
|
+
if (first_row !== undefined) set_active_cell([first_row, col]);
|
|
719
842
|
break;
|
|
843
|
+
}
|
|
720
844
|
case "ArrowLeft":
|
|
721
845
|
e.preventDefault();
|
|
722
846
|
if (col > 0) {
|
|
723
|
-
|
|
724
|
-
|
|
847
|
+
selected_header = col - 1;
|
|
848
|
+
focus_header(col - 1);
|
|
725
849
|
}
|
|
726
850
|
break;
|
|
727
851
|
case "ArrowRight":
|
|
728
852
|
e.preventDefault();
|
|
729
853
|
if (col < num_cols - 1) {
|
|
730
|
-
|
|
731
|
-
|
|
854
|
+
selected_header = col + 1;
|
|
855
|
+
focus_header(col + 1);
|
|
732
856
|
}
|
|
733
857
|
break;
|
|
734
|
-
case "
|
|
858
|
+
case "Enter":
|
|
859
|
+
case "F2":
|
|
735
860
|
e.preventDefault();
|
|
736
|
-
|
|
737
|
-
if (e.shiftKey) {
|
|
738
|
-
if (col > 0) selected = [row, col - 1];
|
|
739
|
-
else if (row > 0) selected = [row - 1, num_cols - 1];
|
|
740
|
-
} else {
|
|
741
|
-
if (col < num_cols - 1) selected = [row, col + 1];
|
|
742
|
-
else if (row < num_rows - 1) selected = [row + 1, 0];
|
|
743
|
-
}
|
|
744
|
-
selected_cells = [selected];
|
|
745
|
-
if (was_editing) {
|
|
746
|
-
const tab_col = (selected as CellCoordinate)[1];
|
|
747
|
-
const tab_static =
|
|
748
|
-
static_columns.includes(tab_col) ||
|
|
749
|
-
static_columns.includes(resolved_headers[tab_col]);
|
|
750
|
-
editing = editable && !tab_static ? selected : false;
|
|
751
|
-
} else {
|
|
752
|
-
editing = false;
|
|
753
|
-
}
|
|
754
|
-
if (!editing) tick().then(() => parent?.focus());
|
|
861
|
+
if (editable && !is_static_column(col)) header_edit = col;
|
|
755
862
|
break;
|
|
756
|
-
|
|
757
|
-
case "
|
|
758
|
-
if (editing && e.shiftKey) {
|
|
759
|
-
// shift+enter inserts newline in textarea — don't intercept
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
863
|
+
case " ":
|
|
864
|
+
case "Spacebar":
|
|
762
865
|
e.preventDefault();
|
|
763
|
-
if (
|
|
764
|
-
|
|
765
|
-
if (row < num_rows - 1) {
|
|
766
|
-
selected = [row + 1, col];
|
|
767
|
-
selected_cells = [selected];
|
|
768
|
-
}
|
|
769
|
-
tick().then(() => parent?.focus());
|
|
770
|
-
} else if (editable) {
|
|
771
|
-
const enter_static =
|
|
772
|
-
static_columns.includes(col) ||
|
|
773
|
-
static_columns.includes(resolved_headers[col]);
|
|
774
|
-
if (!enter_static) {
|
|
775
|
-
editing = [row, col];
|
|
776
|
-
}
|
|
866
|
+
if (editable && !is_static_column(col) && get_dtype(col) === "bool") {
|
|
867
|
+
handle_select_all(col, get_select_all_state(col) !== "checked");
|
|
777
868
|
}
|
|
778
869
|
break;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
870
|
+
}
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (!selected) return;
|
|
875
|
+
const [row, col] = selected;
|
|
876
|
+
const row_position = visible_row_position(row);
|
|
877
|
+
if (row_position === -1) return;
|
|
878
|
+
|
|
879
|
+
if (
|
|
880
|
+
editing &&
|
|
881
|
+
(e.key === "ArrowUp" ||
|
|
882
|
+
e.key === "ArrowDown" ||
|
|
883
|
+
e.key === "ArrowLeft" ||
|
|
884
|
+
e.key === "ArrowRight" ||
|
|
885
|
+
e.key === "Home" ||
|
|
886
|
+
e.key === "End")
|
|
887
|
+
) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
switch (e.key) {
|
|
892
|
+
case "ArrowUp": {
|
|
893
|
+
e.preventDefault();
|
|
894
|
+
if (row_position > 0) {
|
|
895
|
+
set_active_cell([rows[row_position - 1].original._index, col]);
|
|
896
|
+
} else {
|
|
897
|
+
selected = false;
|
|
898
|
+
selected_cells = [];
|
|
899
|
+
selected_header = col;
|
|
900
|
+
focus_header(col);
|
|
901
|
+
}
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
case "ArrowDown":
|
|
905
|
+
e.preventDefault();
|
|
906
|
+
if (row_position < rows.length - 1) {
|
|
907
|
+
set_active_cell([rows[row_position + 1].original._index, col]);
|
|
908
|
+
}
|
|
909
|
+
break;
|
|
910
|
+
case "ArrowLeft":
|
|
911
|
+
e.preventDefault();
|
|
912
|
+
if (col > 0) set_active_cell([row, col - 1]);
|
|
913
|
+
break;
|
|
914
|
+
case "ArrowRight":
|
|
915
|
+
e.preventDefault();
|
|
916
|
+
if (col < num_cols - 1) set_active_cell([row, col + 1]);
|
|
917
|
+
break;
|
|
918
|
+
case "Home":
|
|
919
|
+
e.preventDefault();
|
|
920
|
+
set_active_cell(
|
|
921
|
+
e.ctrlKey || e.metaKey ? [rows[0].original._index, 0] : [row, 0]
|
|
922
|
+
);
|
|
923
|
+
break;
|
|
924
|
+
case "End":
|
|
925
|
+
e.preventDefault();
|
|
926
|
+
set_active_cell(
|
|
927
|
+
e.ctrlKey || e.metaKey
|
|
928
|
+
? [rows[rows.length - 1].original._index, num_cols - 1]
|
|
929
|
+
: [row, num_cols - 1]
|
|
930
|
+
);
|
|
931
|
+
break;
|
|
932
|
+
case "Tab": {
|
|
933
|
+
if (!editing) return;
|
|
934
|
+
e.preventDefault();
|
|
935
|
+
const next_cell = adjacent_visible_cell([row, col], e.shiftKey);
|
|
936
|
+
editing = false;
|
|
937
|
+
if (!next_cell) {
|
|
938
|
+
focus_outside_grid(e.shiftKey);
|
|
782
939
|
break;
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
940
|
+
}
|
|
941
|
+
set_active_cell(next_cell, false);
|
|
942
|
+
const next_col = next_cell[1];
|
|
943
|
+
const next_is_static = is_static_column(next_col);
|
|
944
|
+
if (editable && !next_is_static && get_dtype(next_col) !== "bool") {
|
|
945
|
+
editing = next_cell;
|
|
946
|
+
} else {
|
|
947
|
+
focus_cell(next_cell);
|
|
948
|
+
}
|
|
949
|
+
break;
|
|
950
|
+
}
|
|
951
|
+
case "Enter":
|
|
952
|
+
if (editing && e.shiftKey) return;
|
|
953
|
+
e.preventDefault();
|
|
954
|
+
if (editing) {
|
|
955
|
+
editing = false;
|
|
956
|
+
focus_cell([row, col]);
|
|
957
|
+
} else {
|
|
958
|
+
dispatch_select(row, col);
|
|
959
|
+
const enter_static = is_static_column(col);
|
|
960
|
+
if (editable && !enter_static && get_dtype(col) !== "bool") {
|
|
961
|
+
editing = [row, col];
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
break;
|
|
965
|
+
case "F2":
|
|
966
|
+
e.preventDefault();
|
|
967
|
+
if (editing) {
|
|
968
|
+
editing = false;
|
|
969
|
+
focus_cell([row, col]);
|
|
970
|
+
} else if (
|
|
971
|
+
editable &&
|
|
972
|
+
!is_static_column(col) &&
|
|
973
|
+
get_dtype(col) !== "bool"
|
|
974
|
+
) {
|
|
975
|
+
editing = [row, col];
|
|
976
|
+
}
|
|
977
|
+
break;
|
|
978
|
+
case " ":
|
|
979
|
+
case "Spacebar":
|
|
980
|
+
if (!editing) {
|
|
981
|
+
e.preventDefault();
|
|
982
|
+
const col_is_static = is_static_column(col);
|
|
983
|
+
if (editable && !col_is_static && get_dtype(col) === "bool") {
|
|
984
|
+
const new_values = values.map((value_row) => [...value_row]);
|
|
985
|
+
new_values[row][col] = !new_values[row][col];
|
|
793
986
|
values = new_values;
|
|
794
987
|
push_change(new_values);
|
|
795
988
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
989
|
+
dispatch_select(row, col);
|
|
990
|
+
}
|
|
991
|
+
break;
|
|
992
|
+
case "Escape":
|
|
993
|
+
if (editing) {
|
|
994
|
+
e.preventDefault();
|
|
995
|
+
editing = false;
|
|
996
|
+
focus_cell([row, col]);
|
|
997
|
+
}
|
|
998
|
+
break;
|
|
999
|
+
case "Delete":
|
|
1000
|
+
case "Backspace":
|
|
1001
|
+
if (!editing && editable) {
|
|
1002
|
+
e.preventDefault();
|
|
1003
|
+
const new_values = values.map((value_row) => [...value_row]);
|
|
1004
|
+
selected_cells.forEach(([selected_row, selected_col]) => {
|
|
1005
|
+
if (!is_static_column(selected_col)) {
|
|
1006
|
+
new_values[selected_row][selected_col] = "";
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
values = new_values;
|
|
1010
|
+
push_change(new_values);
|
|
1011
|
+
}
|
|
1012
|
+
break;
|
|
1013
|
+
default:
|
|
1014
|
+
if (
|
|
1015
|
+
editable &&
|
|
1016
|
+
!editing &&
|
|
1017
|
+
e.key.length === 1 &&
|
|
1018
|
+
!e.ctrlKey &&
|
|
1019
|
+
!e.metaKey &&
|
|
1020
|
+
!is_static_column(col) &&
|
|
1021
|
+
get_dtype(col) !== "bool"
|
|
1022
|
+
) {
|
|
1023
|
+
editing = [row, col];
|
|
1024
|
+
}
|
|
1025
|
+
break;
|
|
815
1026
|
}
|
|
1027
|
+
|
|
1028
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "c") handle_copy();
|
|
816
1029
|
}
|
|
817
1030
|
|
|
818
1031
|
function handle_scroll(): void {
|
|
@@ -888,10 +1101,12 @@
|
|
|
888
1101
|
let disable_scroll = $derived(
|
|
889
1102
|
active_cell_menu !== null || active_header_menu !== null
|
|
890
1103
|
);
|
|
891
|
-
let selected_index = $derived(
|
|
1104
|
+
let selected_index = $derived(
|
|
1105
|
+
selected !== false ? visible_row_position(selected[0]) : false
|
|
1106
|
+
);
|
|
892
1107
|
|
|
893
1108
|
$effect(() => {
|
|
894
|
-
if (typeof selected_index === "number") {
|
|
1109
|
+
if (typeof selected_index === "number" && selected_index >= 0) {
|
|
895
1110
|
virtualizer.instance.scrollToIndex(selected_index, { align: "auto" });
|
|
896
1111
|
}
|
|
897
1112
|
});
|
|
@@ -947,7 +1162,11 @@
|
|
|
947
1162
|
class:menu-open={active_cell_menu || active_header_menu}
|
|
948
1163
|
onkeydown={handle_keydown}
|
|
949
1164
|
role="grid"
|
|
950
|
-
|
|
1165
|
+
aria-label={label || "Dataframe"}
|
|
1166
|
+
aria-rowcount={rows.length + 1}
|
|
1167
|
+
aria-colcount={resolved_headers.length + Number(show_row_numbers)}
|
|
1168
|
+
aria-readonly={!editable}
|
|
1169
|
+
tabindex={rows.length === 0 ? 0 : -1}
|
|
951
1170
|
style="--df-max-col-width: {viewport_width}px;"
|
|
952
1171
|
>
|
|
953
1172
|
<Upload
|
|
@@ -961,6 +1180,8 @@
|
|
|
961
1180
|
onload={on_file_upload}
|
|
962
1181
|
bind:dragging
|
|
963
1182
|
aria_label={i18n("dataframe.drop_to_upload")}
|
|
1183
|
+
tab_index={-1}
|
|
1184
|
+
container_element="div"
|
|
964
1185
|
>
|
|
965
1186
|
<div
|
|
966
1187
|
class="virtual-table-viewport"
|
|
@@ -968,18 +1189,19 @@
|
|
|
968
1189
|
bind:this={scroll_container}
|
|
969
1190
|
bind:clientWidth={viewport_width}
|
|
970
1191
|
onscroll={handle_scroll}
|
|
1192
|
+
role="none"
|
|
971
1193
|
style="max-height: {max_height}px;"
|
|
972
|
-
role="grid"
|
|
973
1194
|
>
|
|
974
|
-
{#if label && label.length !== 0}
|
|
975
|
-
<span class="sr-only">{label}</span>
|
|
976
|
-
{/if}
|
|
977
1195
|
<!-- header row: uses table layout to auto-size columns by content -->
|
|
978
|
-
<table class="header-table" bind:this={header_table_el}>
|
|
979
|
-
<thead>
|
|
980
|
-
<tr bind:this={header_row_el}>
|
|
1196
|
+
<table class="header-table" bind:this={header_table_el} role="none">
|
|
1197
|
+
<thead role="rowgroup">
|
|
1198
|
+
<tr bind:this={header_row_el} role="row" aria-rowindex="1">
|
|
981
1199
|
{#if show_row_numbers}
|
|
982
|
-
<th
|
|
1200
|
+
<th
|
|
1201
|
+
class="row-number-header"
|
|
1202
|
+
role="columnheader"
|
|
1203
|
+
aria-colindex="1"> </th
|
|
1204
|
+
>
|
|
983
1205
|
{/if}
|
|
984
1206
|
{#each header_groups as headerGroup (headerGroup.id)}
|
|
985
1207
|
{#each headerGroup.headers as header (header.id)}
|
|
@@ -988,8 +1210,9 @@
|
|
|
988
1210
|
<HeaderCell
|
|
989
1211
|
value={String(header.column.columnDef.header ?? "")}
|
|
990
1212
|
{col_idx}
|
|
1213
|
+
aria_col_index={col_idx + (show_row_numbers ? 2 : 1)}
|
|
991
1214
|
is_editing={header_edit === col_idx}
|
|
992
|
-
is_selected={
|
|
1215
|
+
is_selected={keyboard_header === col_idx}
|
|
993
1216
|
is_static={!!(header.column.columnDef.meta as any)
|
|
994
1217
|
?.isStatic}
|
|
995
1218
|
is_bool={get_dtype(col_idx) === "bool"}
|
|
@@ -1007,6 +1230,7 @@
|
|
|
1007
1230
|
{i18n}
|
|
1008
1231
|
wrap_text={wrap}
|
|
1009
1232
|
onclick={handle_header_click}
|
|
1233
|
+
onfocus={handle_header_focus}
|
|
1010
1234
|
on_menu_click={toggle_header_menu}
|
|
1011
1235
|
on_end_edit={end_header_edit}
|
|
1012
1236
|
on_select_all={handle_select_all}
|
|
@@ -1049,6 +1273,7 @@
|
|
|
1049
1273
|
<!-- table body: absolutely positioned rows (standard tanstack virtual pattern) -->
|
|
1050
1274
|
<div
|
|
1051
1275
|
class="virtual-body"
|
|
1276
|
+
role="none"
|
|
1052
1277
|
style="height: {total_size}px; position: relative; flex-shrink: 0; width: {measurement.total_header_width
|
|
1053
1278
|
? `${measurement.total_header_width}px`
|
|
1054
1279
|
: '100%'};"
|
|
@@ -1061,6 +1286,8 @@
|
|
|
1061
1286
|
class="virtual-row"
|
|
1062
1287
|
class:row-odd={virtual_row.index % 2 !== 0}
|
|
1063
1288
|
data-index={virtual_row.index}
|
|
1289
|
+
role="row"
|
|
1290
|
+
aria-rowindex={virtual_row.index + 2}
|
|
1064
1291
|
style="position: absolute; top: 0; left: 0; width: 100%; transform: translateY({virtual_row.start}px);{selected_cells.some(
|
|
1065
1292
|
([r]) => r === row_idx
|
|
1066
1293
|
)
|
|
@@ -1073,6 +1300,8 @@
|
|
|
1073
1300
|
class="row-number-cell"
|
|
1074
1301
|
data-row={row_idx}
|
|
1075
1302
|
data-col="row-number"
|
|
1303
|
+
role="rowheader"
|
|
1304
|
+
aria-colindex="1"
|
|
1076
1305
|
style="flex: 0 0 {measurement.row_num_width}px; width: {measurement.row_num_width}px;"
|
|
1077
1306
|
>
|
|
1078
1307
|
{row_idx + 1}
|
|
@@ -1091,12 +1320,15 @@
|
|
|
1091
1320
|
datatype={get_dtype(col_idx)}
|
|
1092
1321
|
{row_idx}
|
|
1093
1322
|
{col_idx}
|
|
1323
|
+
aria_col_index={col_idx + (show_row_numbers ? 2 : 1)}
|
|
1094
1324
|
col_style={measurement.get_col_style(ci)}
|
|
1095
1325
|
cell_style={get_styling(row_idx, col_idx)}
|
|
1096
1326
|
selection_classes={is_cell_selected(
|
|
1097
1327
|
[row_idx, col_idx],
|
|
1098
1328
|
selected_cells
|
|
1099
1329
|
)}
|
|
1330
|
+
is_active={is_active_cell(row_idx, col_idx)}
|
|
1331
|
+
aria_row_index={virtual_row.index + 2}
|
|
1100
1332
|
is_editing={!!(
|
|
1101
1333
|
editing &&
|
|
1102
1334
|
editing[0] === row_idx &&
|
|
@@ -1136,11 +1368,12 @@
|
|
|
1136
1368
|
e.preventDefault();
|
|
1137
1369
|
toggle_cell_menu(e, row_idx, col_idx);
|
|
1138
1370
|
}}
|
|
1371
|
+
onfocus={() => handle_cell_focus(row_idx, col_idx)}
|
|
1139
1372
|
onblur={handle_blur}
|
|
1140
1373
|
on_menu_click={(e) => toggle_cell_menu(e, row_idx, col_idx)}
|
|
1141
1374
|
on_select_column={(c) => {
|
|
1142
1375
|
selected_cells = rows.map(
|
|
1143
|
-
(
|
|
1376
|
+
(row) => [row.original._index, c] as CellCoordinate
|
|
1144
1377
|
);
|
|
1145
1378
|
selected = selected_cells[0];
|
|
1146
1379
|
}}
|
|
@@ -1298,7 +1531,7 @@
|
|
|
1298
1531
|
user-select: none;
|
|
1299
1532
|
}
|
|
1300
1533
|
|
|
1301
|
-
.table-wrap > :global(
|
|
1534
|
+
.table-wrap > :global(.upload-container) {
|
|
1302
1535
|
border: 1px solid var(--border-color-primary);
|
|
1303
1536
|
border-radius: var(--table-radius);
|
|
1304
1537
|
overflow: hidden;
|
|
@@ -1338,6 +1571,7 @@
|
|
|
1338
1571
|
/* Header table: auto-sizes columns by content, sticky */
|
|
1339
1572
|
.header-table {
|
|
1340
1573
|
width: 100%;
|
|
1574
|
+
padding: 0;
|
|
1341
1575
|
color: var(--body-text-color);
|
|
1342
1576
|
font-size: var(--input-text-size);
|
|
1343
1577
|
line-height: var(--line-md);
|