@gradio/dataframe 0.14.0 → 0.15.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 +16 -0
- package/Dataframe.stories.svelte +115 -5
- package/Index.svelte +2 -0
- package/dist/Index.svelte +2 -0
- package/dist/Index.svelte.d.ts +4 -0
- package/dist/shared/EditableCell.svelte +52 -7
- package/dist/shared/EditableCell.svelte.d.ts +1 -1
- package/dist/shared/Table.svelte +301 -262
- package/dist/shared/Table.svelte.d.ts +1 -0
- package/dist/shared/selection_utils.d.ts +20 -0
- package/dist/shared/selection_utils.js +111 -0
- package/dist/shared/table_utils.d.ts +7 -1
- package/dist/shared/table_utils.js +96 -10
- package/dist/shared/types.d.ts +2 -0
- package/dist/shared/types.js +1 -0
- package/package.json +5 -5
- package/shared/EditableCell.svelte +58 -7
- package/shared/Table.svelte +342 -329
- package/shared/selection_utils.ts +188 -0
- package/shared/table_utils.ts +120 -10
- package/shared/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @gradio/dataframe
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- [#10456](https://github.com/gradio-app/gradio/pull/10456) [`8e40c15`](https://github.com/gradio-app/gradio/commit/8e40c15669ed1244d6f2288e55c2223279bd37a4) - Implement multiple cell selection. Thanks @hannahblair!
|
|
8
|
+
- [#10463](https://github.com/gradio-app/gradio/pull/10463) [`ed7a091`](https://github.com/gradio-app/gradio/commit/ed7a0919ab6b31184dc4d686b722dbeb013e9ce9) - Expand and collapse dataframe cells. Thanks @hannahblair!
|
|
9
|
+
- [#10478](https://github.com/gradio-app/gradio/pull/10478) [`afb96c6`](https://github.com/gradio-app/gradio/commit/afb96c64451e5a282bfee89445d831d1c87f9746) - Improve dataframe's upload accessibility. Thanks @hannahblair!
|
|
10
|
+
- [#10491](https://github.com/gradio-app/gradio/pull/10491) [`ff5f976`](https://github.com/gradio-app/gradio/commit/ff5f976bbb685fdd4f7c1faeda79e094f55a9f56) - Allow multiline headers in gr.Dataframe. Thanks @hannahblair!
|
|
11
|
+
- [#10494](https://github.com/gradio-app/gradio/pull/10494) [`10932a2`](https://github.com/gradio-app/gradio/commit/10932a291ac7f591bb1d56e4e353b51f10ecc6e3) - Ensure dataframe is not editable when `interactive` is False. Thanks @hannahblair!
|
|
12
|
+
|
|
13
|
+
### Dependency updates
|
|
14
|
+
|
|
15
|
+
- @gradio/client@1.11.0
|
|
16
|
+
- @gradio/upload@0.15.0
|
|
17
|
+
- @gradio/button@0.4.5
|
|
18
|
+
|
|
3
19
|
## 0.14.0
|
|
4
20
|
|
|
5
21
|
### Features
|
package/Dataframe.stories.svelte
CHANGED
|
@@ -93,6 +93,26 @@
|
|
|
93
93
|
row_count: [3, "dynamic"],
|
|
94
94
|
editable: false
|
|
95
95
|
}}
|
|
96
|
+
play={async ({ canvasElement }) => {
|
|
97
|
+
// tests that the cell is not editable
|
|
98
|
+
|
|
99
|
+
const canvas = within(canvasElement);
|
|
100
|
+
const cells = canvas.getAllByRole("cell");
|
|
101
|
+
const initial_value = cells[0].textContent;
|
|
102
|
+
|
|
103
|
+
await userEvent.click(cells[0]);
|
|
104
|
+
await userEvent.keyboard("new value");
|
|
105
|
+
|
|
106
|
+
const final_value = cells[0].textContent;
|
|
107
|
+
if (initial_value !== final_value) {
|
|
108
|
+
throw new Error("Cell content changed when it should be non-editable");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const inputs = canvas.queryAllByRole("textbox");
|
|
112
|
+
if (inputs.length > 0) {
|
|
113
|
+
throw new Error("Input field appeared when table should be non-editable");
|
|
114
|
+
}
|
|
115
|
+
}}
|
|
96
116
|
/>
|
|
97
117
|
|
|
98
118
|
<Story
|
|
@@ -200,9 +220,11 @@
|
|
|
200
220
|
const canvas = within(canvasElement);
|
|
201
221
|
|
|
202
222
|
const cell_400 = canvas.getAllByRole("cell")[5];
|
|
203
|
-
userEvent.click(cell_400);
|
|
223
|
+
await userEvent.click(cell_400);
|
|
204
224
|
|
|
205
|
-
const open_dialog_btn = within(cell_400).
|
|
225
|
+
const open_dialog_btn = await within(cell_400).findByRole("button", {
|
|
226
|
+
name: "⋮"
|
|
227
|
+
});
|
|
206
228
|
await userEvent.click(open_dialog_btn);
|
|
207
229
|
|
|
208
230
|
const add_row_btn = canvas.getByText("Add row above");
|
|
@@ -227,12 +249,52 @@
|
|
|
227
249
|
}}
|
|
228
250
|
/>
|
|
229
251
|
|
|
252
|
+
<Story
|
|
253
|
+
name="Dataframe with multiple selection interactions"
|
|
254
|
+
args={{
|
|
255
|
+
values: [
|
|
256
|
+
[1, 2, 3, 4],
|
|
257
|
+
[5, 6, 7, 8],
|
|
258
|
+
[9, 10, 11, 12],
|
|
259
|
+
[13, 14, 15, 16]
|
|
260
|
+
],
|
|
261
|
+
col_count: [4, "dynamic"],
|
|
262
|
+
row_count: [4, "dynamic"],
|
|
263
|
+
headers: ["A", "B", "C", "D"],
|
|
264
|
+
editable: true
|
|
265
|
+
}}
|
|
266
|
+
play={async ({ canvasElement }) => {
|
|
267
|
+
const canvas = within(canvasElement);
|
|
268
|
+
const cells = canvas.getAllByRole("cell");
|
|
269
|
+
const user = userEvent.setup();
|
|
270
|
+
|
|
271
|
+
// cmd+click to select non-contiguous cells
|
|
272
|
+
await user.keyboard("[MetaLeft>]");
|
|
273
|
+
await user.click(cells[4]);
|
|
274
|
+
await user.click(cells[6]);
|
|
275
|
+
await user.click(cells[2]);
|
|
276
|
+
await user.keyboard("[/MetaLeft]");
|
|
277
|
+
|
|
278
|
+
// shift+click to select a range
|
|
279
|
+
await user.keyboard("[ShiftLeft>]");
|
|
280
|
+
await user.click(cells[7]);
|
|
281
|
+
await user.click(cells[6]);
|
|
282
|
+
await user.keyboard("[/ShiftLeft]");
|
|
283
|
+
|
|
284
|
+
// clear selected cells
|
|
285
|
+
await user.keyboard("{Delete}");
|
|
286
|
+
|
|
287
|
+
// verify cells were cleared by clicking one
|
|
288
|
+
await user.click(cells[2]);
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
|
|
230
292
|
<Story
|
|
231
293
|
name="Dataframe toolbar interactions"
|
|
232
294
|
args={{
|
|
233
295
|
col_count: [3, "dynamic"],
|
|
234
296
|
row_count: [2, "dynamic"],
|
|
235
|
-
headers: ["Math", "Reading", "
|
|
297
|
+
headers: ["Math", "Reading", "Writing"],
|
|
236
298
|
values: [
|
|
237
299
|
[800, 100, 400],
|
|
238
300
|
[200, 800, 700]
|
|
@@ -244,15 +306,63 @@
|
|
|
244
306
|
const canvas = within(canvasElement);
|
|
245
307
|
|
|
246
308
|
const copy_button = canvas.getByRole("button", {
|
|
247
|
-
name:
|
|
309
|
+
name: "Copy table data"
|
|
248
310
|
});
|
|
249
311
|
await userEvent.click(copy_button);
|
|
250
312
|
|
|
251
313
|
const fullscreen_button = canvas.getByRole("button", {
|
|
252
|
-
name:
|
|
314
|
+
name: "Enter fullscreen"
|
|
253
315
|
});
|
|
254
316
|
await userEvent.click(fullscreen_button);
|
|
255
317
|
|
|
256
318
|
await userEvent.click(fullscreen_button);
|
|
257
319
|
}}
|
|
258
320
|
/>
|
|
321
|
+
|
|
322
|
+
<Story
|
|
323
|
+
name="Dataframe with truncated text"
|
|
324
|
+
args={{
|
|
325
|
+
values: [
|
|
326
|
+
[
|
|
327
|
+
"This is a very long text that should be truncated",
|
|
328
|
+
"Short text",
|
|
329
|
+
"Another very long text that needs truncation"
|
|
330
|
+
],
|
|
331
|
+
[
|
|
332
|
+
"Short",
|
|
333
|
+
"This text is also quite long and should be truncated as well",
|
|
334
|
+
"Medium length text here"
|
|
335
|
+
],
|
|
336
|
+
[
|
|
337
|
+
"Medium text",
|
|
338
|
+
"Brief",
|
|
339
|
+
"This is the longest text in the entire table and it should definitely be truncated"
|
|
340
|
+
]
|
|
341
|
+
],
|
|
342
|
+
headers: ["Column A", "Column B", "Column C"],
|
|
343
|
+
label: "Truncated Text Example",
|
|
344
|
+
max_chars: 20,
|
|
345
|
+
col_count: [3, "dynamic"],
|
|
346
|
+
row_count: [3, "dynamic"]
|
|
347
|
+
}}
|
|
348
|
+
/>
|
|
349
|
+
|
|
350
|
+
<Story
|
|
351
|
+
name="Dataframe with multiline headers"
|
|
352
|
+
args={{
|
|
353
|
+
values: [
|
|
354
|
+
[95, 92, 88],
|
|
355
|
+
[89, 90, 85],
|
|
356
|
+
[92, 88, 91]
|
|
357
|
+
],
|
|
358
|
+
headers: [
|
|
359
|
+
"Dataset A\nAccuracy",
|
|
360
|
+
"Dataset B\nPrecision",
|
|
361
|
+
"Dataset C\nRecall"
|
|
362
|
+
],
|
|
363
|
+
label: "Model Metrics",
|
|
364
|
+
col_count: [3, "dynamic"],
|
|
365
|
+
row_count: [3, "dynamic"],
|
|
366
|
+
editable: false
|
|
367
|
+
}}
|
|
368
|
+
/>
|
package/Index.svelte
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
export let loading_status: LoadingStatus;
|
|
50
50
|
export let interactive: boolean;
|
|
51
51
|
export let show_fullscreen_button = false;
|
|
52
|
+
export let max_chars: number | undefined = undefined;
|
|
52
53
|
export let show_copy_button = false;
|
|
53
54
|
|
|
54
55
|
$: _headers = [...(value.headers || headers)];
|
|
@@ -106,6 +107,7 @@
|
|
|
106
107
|
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
107
108
|
bind:value_is_output
|
|
108
109
|
{show_fullscreen_button}
|
|
110
|
+
{max_chars}
|
|
109
111
|
{show_copy_button}
|
|
110
112
|
/>
|
|
111
113
|
</Block>
|
package/dist/Index.svelte
CHANGED
|
@@ -34,6 +34,7 @@ export let max_height = void 0;
|
|
|
34
34
|
export let loading_status;
|
|
35
35
|
export let interactive;
|
|
36
36
|
export let show_fullscreen_button = false;
|
|
37
|
+
export let max_chars = void 0;
|
|
37
38
|
export let show_copy_button = false;
|
|
38
39
|
$:
|
|
39
40
|
_headers = [...value.headers || headers];
|
|
@@ -89,6 +90,7 @@ $:
|
|
|
89
90
|
stream_handler={(...args) => gradio.client.stream(...args)}
|
|
90
91
|
bind:value_is_output
|
|
91
92
|
{show_fullscreen_button}
|
|
93
|
+
{max_chars}
|
|
92
94
|
{show_copy_button}
|
|
93
95
|
/>
|
|
94
96
|
</Block>
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ declare const __propDef: {
|
|
|
38
38
|
loading_status: LoadingStatus;
|
|
39
39
|
interactive: boolean;
|
|
40
40
|
show_fullscreen_button?: boolean | undefined;
|
|
41
|
+
max_chars?: number | undefined;
|
|
41
42
|
show_copy_button?: boolean | undefined;
|
|
42
43
|
};
|
|
43
44
|
events: {
|
|
@@ -136,6 +137,9 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
|
|
|
136
137
|
get show_fullscreen_button(): boolean | undefined;
|
|
137
138
|
/**accessor*/
|
|
138
139
|
set show_fullscreen_button(_: boolean | undefined);
|
|
140
|
+
get max_chars(): number | undefined;
|
|
141
|
+
/**accessor*/
|
|
142
|
+
set max_chars(_: number | undefined);
|
|
139
143
|
get show_copy_button(): boolean | undefined;
|
|
140
144
|
/**accessor*/
|
|
141
145
|
set show_copy_button(_: boolean | undefined);
|
|
@@ -11,10 +11,20 @@ export let clear_on_focus = false;
|
|
|
11
11
|
export let line_breaks = true;
|
|
12
12
|
export let editable = true;
|
|
13
13
|
export let root;
|
|
14
|
+
export let max_chars = null;
|
|
14
15
|
const dispatch = createEventDispatcher();
|
|
16
|
+
let is_expanded = false;
|
|
15
17
|
export let el;
|
|
16
18
|
$:
|
|
17
19
|
_value = value;
|
|
20
|
+
function truncate_text(text, max_length = null) {
|
|
21
|
+
const str = String(text);
|
|
22
|
+
if (!max_length || str.length <= max_length)
|
|
23
|
+
return str;
|
|
24
|
+
return str.slice(0, max_length) + "...";
|
|
25
|
+
}
|
|
26
|
+
$:
|
|
27
|
+
display_text = is_expanded ? value : truncate_text(display_value || value, max_chars);
|
|
18
28
|
function use_focus(node) {
|
|
19
29
|
if (clear_on_focus) {
|
|
20
30
|
_value = "";
|
|
@@ -32,11 +42,20 @@ function handle_blur({
|
|
|
32
42
|
}
|
|
33
43
|
function handle_keydown(event) {
|
|
34
44
|
if (event.key === "Enter") {
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
if (edit) {
|
|
46
|
+
value = _value;
|
|
47
|
+
dispatch("blur");
|
|
48
|
+
} else if (!header) {
|
|
49
|
+
is_expanded = !is_expanded;
|
|
50
|
+
}
|
|
37
51
|
}
|
|
38
52
|
dispatch("keydown", event);
|
|
39
53
|
}
|
|
54
|
+
function handle_click() {
|
|
55
|
+
if (!edit && !header) {
|
|
56
|
+
is_expanded = !is_expanded;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
40
59
|
</script>
|
|
41
60
|
|
|
42
61
|
{#if edit}
|
|
@@ -56,27 +75,31 @@ function handle_keydown(event) {
|
|
|
56
75
|
{/if}
|
|
57
76
|
|
|
58
77
|
<span
|
|
59
|
-
on:
|
|
60
|
-
|
|
78
|
+
on:click={handle_click}
|
|
79
|
+
on:keydown={handle_keydown}
|
|
80
|
+
tabindex="0"
|
|
61
81
|
role="button"
|
|
62
82
|
class:edit
|
|
83
|
+
class:expanded={is_expanded}
|
|
84
|
+
class:multiline={header}
|
|
63
85
|
on:focus|preventDefault
|
|
64
86
|
style={styling}
|
|
65
87
|
class="table-cell-text"
|
|
88
|
+
data-editable={editable}
|
|
66
89
|
placeholder=" "
|
|
67
90
|
>
|
|
68
91
|
{#if datatype === "html"}
|
|
69
|
-
{@html
|
|
92
|
+
{@html display_text}
|
|
70
93
|
{:else if datatype === "markdown"}
|
|
71
94
|
<MarkdownCode
|
|
72
|
-
message={
|
|
95
|
+
message={display_text.toLocaleString()}
|
|
73
96
|
{latex_delimiters}
|
|
74
97
|
{line_breaks}
|
|
75
98
|
chatbot={false}
|
|
76
99
|
{root}
|
|
77
100
|
/>
|
|
78
101
|
{:else}
|
|
79
|
-
{editable ?
|
|
102
|
+
{editable ? display_text : display_value || display_text}
|
|
80
103
|
{/if}
|
|
81
104
|
</span>
|
|
82
105
|
|
|
@@ -97,6 +120,8 @@ function handle_keydown(event) {
|
|
|
97
120
|
|
|
98
121
|
span {
|
|
99
122
|
flex: 1 1 0%;
|
|
123
|
+
position: relative;
|
|
124
|
+
display: inline-block;
|
|
100
125
|
outline: none;
|
|
101
126
|
padding: var(--size-2);
|
|
102
127
|
-webkit-user-select: text;
|
|
@@ -104,11 +129,31 @@ function handle_keydown(event) {
|
|
|
104
129
|
-ms-user-select: text;
|
|
105
130
|
user-select: text;
|
|
106
131
|
cursor: text;
|
|
132
|
+
width: 100%;
|
|
133
|
+
height: 100%;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
input:where(:not(.header), [data-editable="true"]) {
|
|
137
|
+
width: calc(100% - var(--size-10));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
span.expanded {
|
|
141
|
+
height: auto;
|
|
142
|
+
min-height: 100%;
|
|
143
|
+
white-space: pre-wrap;
|
|
144
|
+
word-break: break-word;
|
|
145
|
+
white-space: normal;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.multiline {
|
|
149
|
+
white-space: pre-line;
|
|
107
150
|
}
|
|
108
151
|
|
|
109
152
|
.header {
|
|
110
153
|
transform: translateX(0);
|
|
111
154
|
font-weight: var(--weight-bold);
|
|
155
|
+
white-space: normal;
|
|
156
|
+
word-break: break-word;
|
|
112
157
|
}
|
|
113
158
|
|
|
114
159
|
.edit {
|
|
@@ -16,13 +16,13 @@ declare const __propDef: {
|
|
|
16
16
|
line_breaks?: boolean | undefined;
|
|
17
17
|
editable?: boolean | undefined;
|
|
18
18
|
root: string;
|
|
19
|
+
max_chars?: (number | null) | undefined;
|
|
19
20
|
el: HTMLInputElement | null;
|
|
20
21
|
};
|
|
21
22
|
events: {
|
|
22
23
|
mousedown: MouseEvent;
|
|
23
24
|
mouseup: MouseEvent;
|
|
24
25
|
click: MouseEvent;
|
|
25
|
-
dblclick: MouseEvent;
|
|
26
26
|
focus: FocusEvent;
|
|
27
27
|
blur: CustomEvent<any>;
|
|
28
28
|
keydown: CustomEvent<any>;
|