@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
package/shared/Toolbar.svelte
CHANGED
|
@@ -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(() => {
|
|
@@ -10,15 +10,20 @@ export function is_cell_in_selection(
|
|
|
10
10
|
return selected_cells.some(([r, c]) => r === row && c === col);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// row_above/row_below are the data indices of the rows the caller is rendering
|
|
14
|
+
// on either side of `cell`, or null at an edge of the view. A search or a sort
|
|
15
|
+
// makes them differ from row ± 1, so only the caller knows them.
|
|
13
16
|
export function is_cell_selected(
|
|
14
17
|
cell: CellCoordinate,
|
|
15
|
-
selected_cells: CellCoordinate[]
|
|
18
|
+
selected_cells: CellCoordinate[],
|
|
19
|
+
row_above: number | null,
|
|
20
|
+
row_below: number | null
|
|
16
21
|
): string {
|
|
17
22
|
const [row, col] = cell;
|
|
18
23
|
if (!selected_cells.some(([r, c]) => r === row && c === col)) return "";
|
|
19
24
|
|
|
20
|
-
const up = selected_cells.some(([r, c]) => r ===
|
|
21
|
-
const down = selected_cells.some(([r, c]) => r ===
|
|
25
|
+
const up = selected_cells.some(([r, c]) => r === row_above && c === col);
|
|
26
|
+
const down = selected_cells.some(([r, c]) => r === row_below && c === col);
|
|
22
27
|
const left = selected_cells.some(([r, c]) => r === row && c === col - 1);
|
|
23
28
|
const right = selected_cells.some(([r, c]) => r === row && c === col + 1);
|
|
24
29
|
|
|
@@ -11,18 +11,17 @@ export function make_header_id(col: number): string {
|
|
|
11
11
|
|
|
12
12
|
export async function copy_table_data(
|
|
13
13
|
data: TableData,
|
|
14
|
-
selected_cells: [number, number][]
|
|
14
|
+
selected_cells: [number, number][]
|
|
15
15
|
): Promise<void> {
|
|
16
16
|
if (!data || !data.length) return;
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
selected_cells ||
|
|
20
|
-
data.flatMap((row, r) => row.map((_, c) => [r, c] as [number, number]));
|
|
21
|
-
|
|
22
|
-
const csv = cells_to_copy.reduce(
|
|
18
|
+
const csv = selected_cells.reduce(
|
|
23
19
|
(acc: { [key: string]: { [key: string]: string } }, [row, col]) => {
|
|
24
20
|
acc[row] = acc[row] || {};
|
|
25
|
-
|
|
21
|
+
// only the headers are padded out to the column count on the way in, so
|
|
22
|
+
// a selection can name a cell a short row does not have
|
|
23
|
+
const cell = data[row]?.[col];
|
|
24
|
+
const value = cell ? String(cell.value) : "";
|
|
26
25
|
acc[row][col] =
|
|
27
26
|
value.includes(",") || value.includes('"') || value.includes("\n")
|
|
28
27
|
? `"${value.replace(/"/g, '""')}"`
|
|
@@ -35,7 +34,11 @@ export async function copy_table_data(
|
|
|
35
34
|
const rows = Object.keys(csv).sort((a, b) => +a - +b);
|
|
36
35
|
if (!rows.length) return;
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
// every column any selected row contributes: taking them from the first row
|
|
38
|
+
// alone drops the rest of a selection that is not rectangular there
|
|
39
|
+
const cols = Array.from(
|
|
40
|
+
new Set(rows.flatMap((r) => Object.keys(csv[r])))
|
|
41
|
+
).sort((a, b) => +a - +b);
|
|
39
42
|
const text = rows
|
|
40
43
|
.map((r) => cols.map((c) => csv[r][c] || "").join(","))
|
|
41
44
|
.join("\n");
|
|
@@ -70,11 +70,13 @@ describe("is_cell_in_selection", () => {
|
|
|
70
70
|
|
|
71
71
|
describe("is_cell_selected", () => {
|
|
72
72
|
test("returns empty string when cell not selected", () => {
|
|
73
|
-
expect(is_cell_selected([0, 0], [[1, 1]])).toBe("");
|
|
73
|
+
expect(is_cell_selected([0, 0], [[1, 1]], null, null)).toBe("");
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
test("returns 'cell-selected' for isolated cell", () => {
|
|
77
|
-
expect(is_cell_selected([1, 1], [[1, 1]])).toBe(
|
|
77
|
+
expect(is_cell_selected([1, 1], [[1, 1]], null, null)).toBe(
|
|
78
|
+
"cell-selected"
|
|
79
|
+
);
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
test("adds 'no-top' when neighbor above is selected", () => {
|
|
@@ -83,7 +85,9 @@ describe("is_cell_selected", () => {
|
|
|
83
85
|
[
|
|
84
86
|
[0, 0],
|
|
85
87
|
[1, 0]
|
|
86
|
-
]
|
|
88
|
+
],
|
|
89
|
+
0,
|
|
90
|
+
null
|
|
87
91
|
);
|
|
88
92
|
expect(result).toContain("cell-selected");
|
|
89
93
|
expect(result).toContain("no-top");
|
|
@@ -95,19 +99,46 @@ describe("is_cell_selected", () => {
|
|
|
95
99
|
[
|
|
96
100
|
[0, 0],
|
|
97
101
|
[1, 0]
|
|
98
|
-
]
|
|
102
|
+
],
|
|
103
|
+
null,
|
|
104
|
+
1
|
|
99
105
|
);
|
|
100
106
|
expect(result).toContain("cell-selected");
|
|
101
107
|
expect(result).toContain("no-bottom");
|
|
102
108
|
});
|
|
103
109
|
|
|
110
|
+
test("merges rows that are adjacent in the view but not in the data", () => {
|
|
111
|
+
// what a search leaves behind: data rows 0, 2, 4 shown next to each other
|
|
112
|
+
const selection: CellCoordinate[] = [
|
|
113
|
+
[0, 0],
|
|
114
|
+
[2, 0],
|
|
115
|
+
[4, 0]
|
|
116
|
+
];
|
|
117
|
+
const result = is_cell_selected([2, 0], selection, 0, 4);
|
|
118
|
+
expect(result).toContain("no-top");
|
|
119
|
+
expect(result).toContain("no-bottom");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("does not merge a selected row the view is not showing next to it", () => {
|
|
123
|
+
// row 1 is selected but the search has hidden it, so row 2's neighbours on
|
|
124
|
+
// screen are rows 0 and 4. Comparing row ± 1 instead would merge the top.
|
|
125
|
+
const selection: CellCoordinate[] = [
|
|
126
|
+
[1, 0],
|
|
127
|
+
[2, 0]
|
|
128
|
+
];
|
|
129
|
+
const result = is_cell_selected([2, 0], selection, 0, 4);
|
|
130
|
+
expect(result).toBe("cell-selected");
|
|
131
|
+
});
|
|
132
|
+
|
|
104
133
|
test("adds 'no-left' when neighbor left is selected", () => {
|
|
105
134
|
const result = is_cell_selected(
|
|
106
135
|
[0, 1],
|
|
107
136
|
[
|
|
108
137
|
[0, 0],
|
|
109
138
|
[0, 1]
|
|
110
|
-
]
|
|
139
|
+
],
|
|
140
|
+
null,
|
|
141
|
+
null
|
|
111
142
|
);
|
|
112
143
|
expect(result).toContain("cell-selected");
|
|
113
144
|
expect(result).toContain("no-left");
|
|
@@ -119,7 +150,9 @@ describe("is_cell_selected", () => {
|
|
|
119
150
|
[
|
|
120
151
|
[0, 0],
|
|
121
152
|
[0, 1]
|
|
122
|
-
]
|
|
153
|
+
],
|
|
154
|
+
null,
|
|
155
|
+
null
|
|
123
156
|
);
|
|
124
157
|
expect(result).toContain("cell-selected");
|
|
125
158
|
expect(result).toContain("no-right");
|
|
@@ -133,7 +166,7 @@ describe("is_cell_selected", () => {
|
|
|
133
166
|
[1, 0],
|
|
134
167
|
[1, 1]
|
|
135
168
|
];
|
|
136
|
-
const result = is_cell_selected([1, 1], selection);
|
|
169
|
+
const result = is_cell_selected([1, 1], selection, 0, null);
|
|
137
170
|
expect(result).toContain("cell-selected");
|
|
138
171
|
expect(result).toContain("no-top");
|
|
139
172
|
expect(result).toContain("no-left");
|