@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.
@@ -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<void>;
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(() => {
@@ -27,6 +27,7 @@
27
27
  class="selection-button selection-button-{position} {is_first_position
28
28
  ? `move-${direction}`
29
29
  : ''}"
30
+ tabindex="-1"
30
31
  onclick={(e: MouseEvent) => {
31
32
  e.stopPropagation();
32
33
  on_click && on_click();
@@ -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 === row - 1 && c === col);
21
- const down = selected_cells.some(([r, c]) => r === row + 1 && c === col);
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][] | null
14
+ selected_cells: [number, number][]
15
15
  ): Promise<void> {
16
16
  if (!data || !data.length) return;
17
17
 
18
- const cells_to_copy =
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
- const value = String(data[row][col].value);
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
- const cols = Object.keys(csv[rows[0]]).sort((a, b) => +a - +b);
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("cell-selected");
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");