@gradio/dataframe 0.24.3 → 0.24.5
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 +1048 -68
- 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 +1 -2
- package/dist/shared/EmptyRowButton.svelte +0 -1
- package/dist/shared/HeaderCell.svelte +17 -3
- package/dist/shared/HeaderCell.svelte.d.ts +3 -1
- package/dist/shared/Table.svelte +472 -158
- package/dist/shared/Toolbar.svelte +2 -3
- package/dist/shared/Toolbar.svelte.d.ts +1 -1
- package/dist/shared/icons/SelectionButtons.svelte +1 -0
- package/dist/shared/utils/selection_utils.d.ts +1 -1
- package/dist/shared/utils/selection_utils.js +6 -3
- package/dist/shared/utils/table_utils.d.ts +1 -1
- package/dist/shared/utils/table_utils.js +8 -5
- 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 +1 -2
- package/shared/EmptyRowButton.svelte +0 -1
- package/shared/HeaderCell.svelte +17 -3
- package/shared/Table.svelte +472 -158
- package/shared/Toolbar.svelte +2 -3
- package/shared/icons/SelectionButtons.svelte +1 -0
- package/shared/utils/selection_utils.ts +8 -3
- package/shared/utils/table_utils.ts +11 -8
- package/test/selection_utils.test.ts +40 -7
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
show_copy_button?: boolean;
|
|
19
19
|
show_search?: "none" | "search" | "filter";
|
|
20
20
|
fullscreen?: boolean;
|
|
21
|
-
on_copy: () => Promise<
|
|
21
|
+
on_copy: () => Promise<boolean>;
|
|
22
22
|
on_commit_filter: () => void;
|
|
23
23
|
current_search_query?: string | null;
|
|
24
24
|
onsearch?: (query: string | null) => void;
|
|
@@ -48,8 +48,7 @@
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
async function handle_copy(): Promise<void> {
|
|
51
|
-
await on_copy();
|
|
52
|
-
copy_feedback();
|
|
51
|
+
if (await on_copy()) copy_feedback();
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
$effect(() => {
|
|
@@ -3,7 +3,7 @@ type $$ComponentProps = {
|
|
|
3
3
|
show_copy_button?: boolean;
|
|
4
4
|
show_search?: "none" | "search" | "filter";
|
|
5
5
|
fullscreen?: boolean;
|
|
6
|
-
on_copy: () => Promise<
|
|
6
|
+
on_copy: () => Promise<boolean>;
|
|
7
7
|
on_commit_filter: () => void;
|
|
8
8
|
current_search_query?: string | null;
|
|
9
9
|
onsearch?: (query: string | null) => void;
|
|
@@ -4,7 +4,7 @@ export type CellData = {
|
|
|
4
4
|
value: CellValue;
|
|
5
5
|
};
|
|
6
6
|
export declare function is_cell_in_selection(coords: [number, number], selected_cells: [number, number][]): boolean;
|
|
7
|
-
export declare function is_cell_selected(cell: CellCoordinate, selected_cells: CellCoordinate[]): string;
|
|
7
|
+
export declare function is_cell_selected(cell: CellCoordinate, selected_cells: CellCoordinate[], row_above: number | null, row_below: number | null): string;
|
|
8
8
|
export declare function get_range_selection(start: CellCoordinate, end: CellCoordinate): CellCoordinate[];
|
|
9
9
|
export declare function handle_selection(current: CellCoordinate, selected_cells: CellCoordinate[], event: {
|
|
10
10
|
shiftKey: boolean;
|
|
@@ -2,12 +2,15 @@ export function is_cell_in_selection(coords, selected_cells) {
|
|
|
2
2
|
const [row, col] = coords;
|
|
3
3
|
return selected_cells.some(([r, c]) => r === row && c === col);
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
// row_above/row_below are the data indices of the rows the caller is rendering
|
|
6
|
+
// on either side of `cell`, or null at an edge of the view. A search or a sort
|
|
7
|
+
// makes them differ from row ± 1, so only the caller knows them.
|
|
8
|
+
export function is_cell_selected(cell, selected_cells, row_above, row_below) {
|
|
6
9
|
const [row, col] = cell;
|
|
7
10
|
if (!selected_cells.some(([r, c]) => r === row && c === col))
|
|
8
11
|
return "";
|
|
9
|
-
const up = selected_cells.some(([r, c]) => r ===
|
|
10
|
-
const down = selected_cells.some(([r, c]) => r ===
|
|
12
|
+
const up = selected_cells.some(([r, c]) => r === row_above && c === col);
|
|
13
|
+
const down = selected_cells.some(([r, c]) => r === row_below && c === col);
|
|
11
14
|
const left = selected_cells.some(([r, c]) => r === row && c === col - 1);
|
|
12
15
|
const right = selected_cells.some(([r, c]) => r === row && c === col + 1);
|
|
13
16
|
return `cell-selected${up ? " no-top" : ""}${down ? " no-bottom" : ""}${left ? " no-left" : ""}${right ? " no-right" : ""}`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CellValue, Headers, HeadersWithIDs, TableData } from "../types";
|
|
2
2
|
export declare function make_cell_id(row: number, col: number): string;
|
|
3
3
|
export declare function make_header_id(col: number): string;
|
|
4
|
-
export declare function copy_table_data(data: TableData, selected_cells: [number, number][]
|
|
4
|
+
export declare function copy_table_data(data: TableData, selected_cells: [number, number][]): Promise<void>;
|
|
5
5
|
export declare function guess_delimiter(text: string, possibleDelimiters: string[]): string[];
|
|
6
6
|
export declare function data_uri_to_blob(data_uri: string): Blob;
|
|
7
7
|
export declare function handle_file_upload(data_uri: string, update_headers: (headers: Headers) => HeadersWithIDs[], update_values: (values: CellValue[][]) => void): void;
|
|
@@ -8,11 +8,12 @@ export function make_header_id(col) {
|
|
|
8
8
|
export async function copy_table_data(data, selected_cells) {
|
|
9
9
|
if (!data || !data.length)
|
|
10
10
|
return;
|
|
11
|
-
const
|
|
12
|
-
data.flatMap((row, r) => row.map((_, c) => [r, c]));
|
|
13
|
-
const csv = cells_to_copy.reduce((acc, [row, col]) => {
|
|
11
|
+
const csv = selected_cells.reduce((acc, [row, col]) => {
|
|
14
12
|
acc[row] = acc[row] || {};
|
|
15
|
-
|
|
13
|
+
// only the headers are padded out to the column count on the way in, so
|
|
14
|
+
// a selection can name a cell a short row does not have
|
|
15
|
+
const cell = data[row]?.[col];
|
|
16
|
+
const value = cell ? String(cell.value) : "";
|
|
16
17
|
acc[row][col] =
|
|
17
18
|
value.includes(",") || value.includes('"') || value.includes("\n")
|
|
18
19
|
? `"${value.replace(/"/g, '""')}"`
|
|
@@ -22,7 +23,9 @@ export async function copy_table_data(data, selected_cells) {
|
|
|
22
23
|
const rows = Object.keys(csv).sort((a, b) => +a - +b);
|
|
23
24
|
if (!rows.length)
|
|
24
25
|
return;
|
|
25
|
-
|
|
26
|
+
// every column any selected row contributes: taking them from the first row
|
|
27
|
+
// alone drops the rest of a selection that is not rectangular there
|
|
28
|
+
const cols = Array.from(new Set(rows.flatMap((r) => Object.keys(csv[r])))).sort((a, b) => +a - +b);
|
|
26
29
|
const text = rows
|
|
27
30
|
.map((r) => cols.map((c) => csv[r][c] || "").join(","))
|
|
28
31
|
.join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/dataframe",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.5",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
"dequal": "^2.0.3",
|
|
16
16
|
"@tanstack/table-core": "^8.21.3",
|
|
17
17
|
"@tanstack/virtual-core": "^3.13.6",
|
|
18
|
-
"@gradio/atoms": "^0.26.
|
|
18
|
+
"@gradio/atoms": "^0.26.2",
|
|
19
|
+
"@gradio/checkbox": "^0.8.3",
|
|
19
20
|
"@gradio/button": "^0.8.2",
|
|
20
|
-
"@gradio/
|
|
21
|
+
"@gradio/client": "^2.7.0",
|
|
22
|
+
"@gradio/markdown-code": "^0.10.1",
|
|
21
23
|
"@gradio/icons": "^0.16.0",
|
|
22
|
-
"@gradio/
|
|
23
|
-
"@gradio/client": "^2.5.0",
|
|
24
|
+
"@gradio/upload": "^0.18.3",
|
|
24
25
|
"@gradio/statustracker": "^0.15.3",
|
|
25
|
-
"@gradio/
|
|
26
|
-
"@gradio/utils": "^0.14.0"
|
|
26
|
+
"@gradio/utils": "^0.14.2"
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"svelte": "^5.48.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@gradio/preview": "^0.17.
|
|
45
|
+
"@gradio/preview": "^0.17.1"
|
|
46
46
|
},
|
|
47
47
|
"repository": {
|
|
48
48
|
"type": "git",
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
let {
|
|
2
|
+
let {
|
|
3
|
+
on_click,
|
|
4
|
+
tab_index = undefined
|
|
5
|
+
}: {
|
|
6
|
+
on_click: (event: MouseEvent) => void;
|
|
7
|
+
tab_index?: number;
|
|
8
|
+
} = $props();
|
|
3
9
|
</script>
|
|
4
10
|
|
|
5
11
|
<button
|
|
6
12
|
aria-label="Open cell menu"
|
|
7
13
|
class="cell-menu-button"
|
|
8
14
|
aria-haspopup="menu"
|
|
15
|
+
tabindex={tab_index}
|
|
9
16
|
onclick={on_click}
|
|
10
17
|
ontouchstart={(event) => {
|
|
11
18
|
event.preventDefault();
|
package/shared/DataCell.svelte
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
datatype = "str",
|
|
12
12
|
row_idx,
|
|
13
13
|
col_idx,
|
|
14
|
+
aria_col_index = col_idx + 1,
|
|
14
15
|
col_style = "",
|
|
15
16
|
cell_style = "",
|
|
16
17
|
selection_classes = "",
|
|
18
|
+
is_active = false,
|
|
19
|
+
aria_row_index,
|
|
17
20
|
is_editing = false,
|
|
18
21
|
is_flash = false,
|
|
19
22
|
is_static = false,
|
|
@@ -32,6 +35,7 @@
|
|
|
32
35
|
onmousedown,
|
|
33
36
|
ondblclick,
|
|
34
37
|
oncontextmenu,
|
|
38
|
+
onfocus,
|
|
35
39
|
onblur,
|
|
36
40
|
on_menu_click,
|
|
37
41
|
on_select_column,
|
|
@@ -42,9 +46,12 @@
|
|
|
42
46
|
datatype?: Datatype;
|
|
43
47
|
row_idx: number;
|
|
44
48
|
col_idx: number;
|
|
49
|
+
aria_col_index?: number;
|
|
45
50
|
col_style?: string;
|
|
46
51
|
cell_style?: string;
|
|
47
52
|
selection_classes?: string;
|
|
53
|
+
is_active?: boolean;
|
|
54
|
+
aria_row_index: number;
|
|
48
55
|
is_editing?: boolean;
|
|
49
56
|
is_flash?: boolean;
|
|
50
57
|
is_static?: boolean;
|
|
@@ -63,6 +70,7 @@
|
|
|
63
70
|
onmousedown: (event: MouseEvent) => void;
|
|
64
71
|
ondblclick: (event: MouseEvent) => void;
|
|
65
72
|
oncontextmenu: (event: MouseEvent) => void;
|
|
73
|
+
onfocus: () => void;
|
|
66
74
|
onblur: (detail: {
|
|
67
75
|
blur_event: FocusEvent;
|
|
68
76
|
coords: [number, number];
|
|
@@ -98,10 +106,15 @@
|
|
|
98
106
|
data-row={row_idx}
|
|
99
107
|
data-col={col_idx}
|
|
100
108
|
data-testid={`cell-${row_idx}-${col_idx}`}
|
|
101
|
-
|
|
109
|
+
role="gridcell"
|
|
110
|
+
aria-rowindex={aria_row_index}
|
|
111
|
+
aria-colindex={aria_col_index}
|
|
112
|
+
aria-selected={is_selected}
|
|
113
|
+
tabindex={is_active ? 0 : -1}
|
|
102
114
|
{onmousedown}
|
|
103
115
|
{ondblclick}
|
|
104
116
|
{oncontextmenu}
|
|
117
|
+
{onfocus}
|
|
105
118
|
style="{col_style} {cell_style}"
|
|
106
119
|
>
|
|
107
120
|
<div class="cell-wrap" bind:this={wrap_el} style={locked_height}>
|
|
@@ -127,7 +140,7 @@
|
|
|
127
140
|
expanded={is_solo}
|
|
128
141
|
/>
|
|
129
142
|
{#if show_menu_button}
|
|
130
|
-
<CellMenuButton on_click={on_menu_click} />
|
|
143
|
+
<CellMenuButton on_click={on_menu_click} tab_index={-1} />
|
|
131
144
|
{/if}
|
|
132
145
|
</div>
|
|
133
146
|
</div>
|
|
@@ -146,6 +159,17 @@
|
|
|
146
159
|
min-width: 0;
|
|
147
160
|
}
|
|
148
161
|
|
|
162
|
+
.body-cell:focus-visible {
|
|
163
|
+
outline: 2px solid var(--color-accent);
|
|
164
|
+
outline-offset: -2px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@media (forced-colors: active) {
|
|
168
|
+
.body-cell:focus-visible {
|
|
169
|
+
outline-color: CanvasText;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
149
173
|
.body-cell.static {
|
|
150
174
|
user-select: text;
|
|
151
175
|
}
|
package/shared/HeaderCell.svelte
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
let {
|
|
13
13
|
value,
|
|
14
14
|
col_idx,
|
|
15
|
+
aria_col_index = col_idx + 1,
|
|
15
16
|
is_editing = false,
|
|
16
17
|
is_selected = false,
|
|
17
18
|
is_static = false,
|
|
@@ -30,12 +31,14 @@
|
|
|
30
31
|
i18n,
|
|
31
32
|
wrap_text = false,
|
|
32
33
|
onclick,
|
|
34
|
+
onfocus,
|
|
33
35
|
on_menu_click,
|
|
34
36
|
on_end_edit,
|
|
35
37
|
on_select_all
|
|
36
38
|
}: {
|
|
37
39
|
value: string;
|
|
38
40
|
col_idx: number;
|
|
41
|
+
aria_col_index?: number;
|
|
39
42
|
is_editing?: boolean;
|
|
40
43
|
is_selected?: boolean;
|
|
41
44
|
is_static?: boolean;
|
|
@@ -54,8 +57,9 @@
|
|
|
54
57
|
i18n: I18nFormatter;
|
|
55
58
|
wrap_text?: boolean;
|
|
56
59
|
onclick: (event: MouseEvent, col: number) => void;
|
|
60
|
+
onfocus: (col: number) => void;
|
|
57
61
|
on_menu_click: (event: MouseEvent, col: number) => void;
|
|
58
|
-
on_end_edit: (
|
|
62
|
+
on_end_edit: (event: KeyboardEvent) => void;
|
|
59
63
|
on_select_all?: (col: number, checked: boolean) => void;
|
|
60
64
|
} = $props();
|
|
61
65
|
|
|
@@ -71,6 +75,11 @@
|
|
|
71
75
|
class:filtered={is_filtered}
|
|
72
76
|
class:first-column={is_first_column}
|
|
73
77
|
data-heading={col_idx}
|
|
78
|
+
data-testid={`header-${col_idx}`}
|
|
79
|
+
role="columnheader"
|
|
80
|
+
aria-colindex={aria_col_index}
|
|
81
|
+
tabindex={is_selected ? 0 : -1}
|
|
82
|
+
onfocus={() => onfocus(col_idx)}
|
|
74
83
|
onclick={(e) => onclick(e, col_idx)}
|
|
75
84
|
onmousedown={(e) => {
|
|
76
85
|
e.preventDefault();
|
|
@@ -94,6 +103,7 @@
|
|
|
94
103
|
indeterminate={select_all_state === "indeterminate"}
|
|
95
104
|
label={`Toggle all: ${value}`}
|
|
96
105
|
interactive={true}
|
|
106
|
+
tab_index={-1}
|
|
97
107
|
on_select={() =>
|
|
98
108
|
on_select_all?.(col_idx, select_all_state !== "checked")}
|
|
99
109
|
/>
|
|
@@ -106,7 +116,8 @@
|
|
|
106
116
|
edit={is_editing}
|
|
107
117
|
onkeydown={(event) => {
|
|
108
118
|
if (["Enter", "Escape", "Tab"].includes(event.key)) {
|
|
109
|
-
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
on_end_edit(event);
|
|
110
121
|
}
|
|
111
122
|
}}
|
|
112
123
|
header
|
|
@@ -148,7 +159,10 @@
|
|
|
148
159
|
</span>
|
|
149
160
|
{/if}
|
|
150
161
|
{#if show_menu_button}
|
|
151
|
-
<CellMenuButton
|
|
162
|
+
<CellMenuButton
|
|
163
|
+
on_click={(e) => on_menu_click(e, col_idx)}
|
|
164
|
+
tab_index={-1}
|
|
165
|
+
/>
|
|
152
166
|
{/if}
|
|
153
167
|
</div>
|
|
154
168
|
</th>
|