@gradio/dataframe 0.15.0 → 0.16.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/Dataframe.stories.svelte +183 -2
  3. package/Example.svelte +7 -0
  4. package/Index.svelte +20 -3
  5. package/dist/Example.svelte +7 -0
  6. package/dist/Index.svelte +16 -4
  7. package/dist/Index.svelte.d.ts +12 -0
  8. package/dist/shared/CellMenu.svelte +1 -1
  9. package/dist/shared/EditableCell.svelte +1 -6
  10. package/dist/shared/Table.svelte +620 -319
  11. package/dist/shared/Table.svelte.d.ts +3 -0
  12. package/dist/shared/Toolbar.svelte +122 -30
  13. package/dist/shared/Toolbar.svelte.d.ts +4 -0
  14. package/dist/shared/VirtualTable.svelte +70 -26
  15. package/dist/shared/VirtualTable.svelte.d.ts +1 -0
  16. package/dist/shared/icons/FilterIcon.svelte +11 -0
  17. package/dist/shared/icons/FilterIcon.svelte.d.ts +16 -0
  18. package/dist/shared/icons/SortIcon.svelte +90 -0
  19. package/dist/shared/icons/SortIcon.svelte.d.ts +20 -0
  20. package/dist/shared/selection_utils.d.ts +12 -2
  21. package/dist/shared/selection_utils.js +33 -5
  22. package/dist/shared/types.d.ts +16 -0
  23. package/dist/shared/types.js +1 -0
  24. package/dist/shared/utils/menu_utils.d.ts +42 -0
  25. package/dist/shared/utils/menu_utils.js +58 -0
  26. package/dist/shared/utils/sort_utils.d.ts +7 -0
  27. package/dist/shared/utils/sort_utils.js +39 -0
  28. package/dist/shared/utils/table_utils.d.ts +12 -0
  29. package/dist/shared/utils/table_utils.js +148 -0
  30. package/package.json +8 -8
  31. package/shared/CellMenu.svelte +1 -1
  32. package/shared/EditableCell.svelte +1 -6
  33. package/shared/Table.svelte +649 -322
  34. package/shared/Toolbar.svelte +125 -30
  35. package/shared/VirtualTable.svelte +73 -26
  36. package/shared/icons/FilterIcon.svelte +12 -0
  37. package/shared/icons/SortIcon.svelte +95 -0
  38. package/shared/selection_utils.ts +51 -9
  39. package/shared/types.ts +27 -0
  40. package/shared/utils/menu_utils.ts +115 -0
  41. package/shared/utils/sort_utils.test.ts +71 -0
  42. package/shared/utils/sort_utils.ts +55 -0
  43. package/shared/utils/table_utils.test.ts +114 -0
  44. package/shared/utils/table_utils.ts +206 -0
  45. package/dist/shared/table_utils.d.ts +0 -12
  46. package/dist/shared/table_utils.js +0 -113
  47. package/shared/table_utils.ts +0 -148
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @gradio/dataframe
2
2
 
3
+ ## 0.16.1
4
+
5
+ ### Fixes
6
+
7
+ - [#10607](https://github.com/gradio-app/gradio/pull/10607) [`c354f5f`](https://github.com/gradio-app/gradio/commit/c354f5ff16c787d722f4e53d5a97f729abba955e) - Add empty dataframe functionality. Thanks @hannahblair!
8
+ - [#10596](https://github.com/gradio-app/gradio/pull/10596) [`a8bde76`](https://github.com/gradio-app/gradio/commit/a8bde76e2b0f65b3565019beb03ac8b1fd152963) - Fix margin above `gr.Dataframe` when no header is provided. Thanks @abidlabs!
9
+
10
+ ## 0.16.0
11
+
12
+ ### Features
13
+
14
+ - [#10561](https://github.com/gradio-app/gradio/pull/10561) [`26494ce`](https://github.com/gradio-app/gradio/commit/26494cea570ffe0ead1f8b7a7135ab5a89c6bcbd) - Allow freezing columns in `gr.Dataframe`. Thanks @hannahblair!
15
+ - [#10554](https://github.com/gradio-app/gradio/pull/10554) [`b8ff5d6`](https://github.com/gradio-app/gradio/commit/b8ff5d6bfe1a9f3379580754b1e23857e2f0c96b) - Add optional search bar to `gr.Dataframe`'s toolbar. Thanks @hannahblair!
16
+ - [#10529](https://github.com/gradio-app/gradio/pull/10529) [`196b600`](https://github.com/gradio-app/gradio/commit/196b600b3962b85781b53c512e17708644b86f6f) - Select entire row or column in dataframe. Thanks @hannahblair!
17
+ - [#10558](https://github.com/gradio-app/gradio/pull/10558) [`1113002`](https://github.com/gradio-app/gradio/commit/111300242fdf135724a304920a93fc34a8037f7d) - Fix spacing issue with `gr.Dataframe` in Safari. Thanks @hannahblair!
18
+ - [#10553](https://github.com/gradio-app/gradio/pull/10553) [`4c08b9f`](https://github.com/gradio-app/gradio/commit/4c08b9f3c0bcafb0edc56330d8d81e78a6e3763b) - Prevent scrolling when the dataframe cell menu is open. Thanks @hannahblair!
19
+ - [#10541](https://github.com/gradio-app/gradio/pull/10541) [`e505fab`](https://github.com/gradio-app/gradio/commit/e505fabecb17c50e073483ed7d6aab2e04c9fcf2) - Add copy button feedback to `gr.Dataframe`. Thanks @hannahblair!
20
+ - [#10540](https://github.com/gradio-app/gradio/pull/10540) [`deeebfb`](https://github.com/gradio-app/gradio/commit/deeebfba46f15bb3641b86e25156215d2d727087) - Revert editable text changes. Thanks @hannahblair!
21
+
22
+ ### Fixes
23
+
24
+ - [#10490](https://github.com/gradio-app/gradio/pull/10490) [`178311b`](https://github.com/gradio-app/gradio/commit/178311b72d72a3c5f4a67bee5e0098be4232e68c) - Ensure row numbers functionality in dataframe works as expected. Thanks @hannahblair!
25
+ - [#10535](https://github.com/gradio-app/gradio/pull/10535) [`d909868`](https://github.com/gradio-app/gradio/commit/d9098681f8883686a617c8f98b22c77057febed1) - Ensure `max_height` is applied in `gr.Dataframe`. Thanks @hannahblair!
26
+ - [#10521](https://github.com/gradio-app/gradio/pull/10521) [`79937fd`](https://github.com/gradio-app/gradio/commit/79937fd76021b31abdbc3f8f2c32ef123fd676aa) - Change word-break prop in dataframe headers. Thanks @hannahblair!
27
+ - [#10520](https://github.com/gradio-app/gradio/pull/10520) [`2a1fc2a`](https://github.com/gradio-app/gradio/commit/2a1fc2a92888f622579e4b2daf86be487c73004d) - Ensure links work as expected in dataframe. Thanks @hannahblair!
28
+
29
+ ### Dependency updates
30
+
31
+ - @gradio/statustracker@0.10.3
32
+ - @gradio/atoms@0.13.2
33
+ - @gradio/utils@0.10.1
34
+ - @gradio/client@1.12.0
35
+ - @gradio/markdown-code@0.4.0
36
+ - @gradio/upload@0.15.1
37
+ - @gradio/button@0.4.6
38
+
3
39
  ## 0.15.0
4
40
 
5
41
  ### Features
@@ -152,6 +152,21 @@
152
152
  }}
153
153
  />
154
154
 
155
+ <Story
156
+ name="Dataframe without a label"
157
+ args={{
158
+ values: [
159
+ [800, 100, 800],
160
+ [200, 800, 700]
161
+ ],
162
+ headers: ["Math", "Reading", "Writing"],
163
+ show_label: false,
164
+ col_count: [3, "dynamic"],
165
+ row_count: [2, "dynamic"],
166
+ editable: false
167
+ }}
168
+ />
169
+
155
170
  <Story
156
171
  name="Dataframe with different colors"
157
172
  args={{
@@ -204,6 +219,18 @@
204
219
  }}
205
220
  />
206
221
 
222
+ <Story
223
+ name="Dataframe with link"
224
+ args={{
225
+ values: [['<a href="https://www.google.com/">google</a>']],
226
+ headers: ["link"],
227
+ datatype: ["markdown"],
228
+ interactive: false,
229
+ col_count: [1, "dynamic"],
230
+ row_count: [1, "dynamic"]
231
+ }}
232
+ />
233
+
207
234
  <Story
208
235
  name="Dataframe with dialog interactions"
209
236
  args={{
@@ -236,7 +263,7 @@
236
263
  />
237
264
 
238
265
  <Story
239
- name="Dataframe with fullscreen button"
266
+ name="Dataframe with fullscreen button and label and search"
240
267
  args={{
241
268
  col_count: [3, "dynamic"],
242
269
  row_count: [2, "dynamic"],
@@ -245,7 +272,11 @@
245
272
  [800, 100, 400],
246
273
  [200, 800, 700]
247
274
  ],
248
- show_fullscreen_button: true
275
+ show_fullscreen_button: true,
276
+ show_label: true,
277
+ show_copy_button: true,
278
+ show_search: "search",
279
+ label: "Test scores"
249
280
  }}
250
281
  />
251
282
 
@@ -319,6 +350,25 @@
319
350
  }}
320
351
  />
321
352
 
353
+ <Story
354
+ name="Dataframe with row numbers"
355
+ args={{
356
+ values: [
357
+ [95, 92, 88],
358
+ [89, 90, 85],
359
+ [92, 88, 91],
360
+ [87, 85, 89],
361
+ [91, 93, 90]
362
+ ],
363
+ headers: ["Model A", "Model B", "Model C"],
364
+ label: "Model Performance",
365
+ col_count: [3, "dynamic"],
366
+ row_count: [5, "dynamic"],
367
+ show_row_numbers: true,
368
+ editable: false
369
+ }}
370
+ />
371
+
322
372
  <Story
323
373
  name="Dataframe with truncated text"
324
374
  args={{
@@ -366,3 +416,134 @@
366
416
  editable: false
367
417
  }}
368
418
  />
419
+
420
+ <Story
421
+ name="Dataframe with row and column selection"
422
+ args={{
423
+ values: [
424
+ [1, 2, 3, 4],
425
+ [5, 6, 7, 8],
426
+ [9, 10, 11, 12],
427
+ [13, 14, 15, 16]
428
+ ],
429
+ col_count: [4, "dynamic"],
430
+ row_count: [4, "dynamic"],
431
+ headers: ["A", "B", "C", "D"],
432
+ editable: true
433
+ }}
434
+ play={async ({ canvasElement }) => {
435
+ const canvas = within(canvasElement);
436
+ const user = userEvent.setup();
437
+
438
+ const grid = canvas.getByRole("grid");
439
+ await user.click(grid);
440
+
441
+ const cells = canvas.getAllByRole("cell");
442
+ await user.click(cells[5]); // Click cell with value 6
443
+
444
+ const row_button = await canvas.findByRole("button", {
445
+ name: "Select row"
446
+ });
447
+ await user.click(row_button);
448
+
449
+ await user.click(cells[6]);
450
+
451
+ const col_button = await canvas.findByRole("button", {
452
+ name: "Select column"
453
+ });
454
+ await user.click(col_button);
455
+
456
+ await user.keyboard("{Delete}");
457
+ }}
458
+ />
459
+
460
+ <Story
461
+ name="Dataframe with lots of values"
462
+ args={{
463
+ values: [
464
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
465
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
466
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
467
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
468
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
469
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
470
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
471
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
472
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
473
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
474
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
475
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
476
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
477
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
478
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
479
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
480
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
481
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
482
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
483
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
484
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
485
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
486
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
487
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
488
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
489
+ ],
490
+ col_count: [10, "dynamic"],
491
+ row_count: [10, "dynamic"],
492
+ max_height: 700
493
+ }}
494
+ />
495
+
496
+ <Story
497
+ name="Dataframe with search and filter"
498
+ args={{
499
+ values: [
500
+ ["Cat", 5, "Pet"],
501
+ ["Horse", 3, "Farm"],
502
+ ["Snake", 1, "Pet"],
503
+ ["Cow", 4, "Farm"],
504
+ ["Dog", 6, "Pet"]
505
+ ],
506
+ headers: ["Animal", "Count", "Type"],
507
+ col_count: [3, "dynamic"],
508
+ row_count: [5, "dynamic"],
509
+ show_search: "filter",
510
+ editable: false
511
+ }}
512
+ play={async ({ canvasElement }) => {
513
+ const canvas = within(canvasElement);
514
+ const user = userEvent.setup();
515
+
516
+ const search_input = canvas.getByPlaceholderText("Search...");
517
+ await user.type(search_input, "Pet");
518
+
519
+ await new Promise((resolve) => setTimeout(resolve, 100));
520
+
521
+ const filter_button = canvas.getByLabelText(
522
+ "Apply filter and update dataframe values"
523
+ );
524
+ await user.click(filter_button);
525
+
526
+ await new Promise((resolve) => setTimeout(resolve, 100));
527
+ }}
528
+ />
529
+
530
+ <Story
531
+ name="Dataframe with frozen columns"
532
+ args={{
533
+ values: [
534
+ ["ID", "Name", "Age", "City", "Country", "Score"],
535
+ ["1", "John", "25", "New York", "USA", "95"],
536
+ ["2", "Emma", "30", "London", "UK", "88"],
537
+ ["3", "Luis", "28", "Madrid", "Spain", "92"],
538
+ ["4", "Anna", "35", "Paris", "France", "90"],
539
+ ["5", "Chen", "27", "Beijing", "China", "94"]
540
+ ],
541
+ headers: ["ID", "Name", "Age", "City", "Country", "Score"],
542
+ label: "User Data",
543
+ col_count: [6, "dynamic"],
544
+ row_count: [6, "dynamic"],
545
+ pinned_columns: 2,
546
+ show_row_numbers: true,
547
+ editable: false
548
+ }}
549
+ />
package/Example.svelte CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  let hovered = false;
8
8
  let loaded = Array.isArray(value);
9
+ let is_empty = loaded && (value.length === 0 || value[0].length === 0);
9
10
  </script>
10
11
 
11
12
  {#if loaded}
@@ -20,6 +21,12 @@
20
21
  >
21
22
  {#if typeof value === "string"}
22
23
  {value}
24
+ {:else if is_empty}
25
+ <table class="">
26
+ <tr>
27
+ <td>Empty</td>
28
+ </tr>
29
+ </table>
23
30
  {:else}
24
31
  <table class="">
25
32
  {#each value.slice(0, 3) as row, i}
package/Index.svelte CHANGED
@@ -51,9 +51,22 @@
51
51
  export let show_fullscreen_button = false;
52
52
  export let max_chars: number | undefined = undefined;
53
53
  export let show_copy_button = false;
54
+ export let show_row_numbers = false;
55
+ export let show_search: "none" | "search" | "filter" = "none";
56
+
57
+ let search_query: string | null = null;
58
+ $: filtered_cell_values = search_query
59
+ ? value.data?.filter((row) =>
60
+ row.some(
61
+ (cell) =>
62
+ search_query &&
63
+ String(cell).toLowerCase().includes(search_query.toLowerCase())
64
+ )
65
+ )
66
+ : null;
67
+ export let pinned_columns = 0;
54
68
 
55
69
  $: _headers = [...(value.headers || headers)];
56
- $: cell_values = value.data ? [...value.data] : [];
57
70
  $: display_value = value?.metadata?.display_value
58
71
  ? [...value?.metadata?.display_value]
59
72
  : null;
@@ -71,7 +84,7 @@
71
84
  container={false}
72
85
  {scale}
73
86
  {min_width}
74
- allow_overflow={false}
87
+ overflow_behavior="visible"
75
88
  >
76
89
  <StatusTracker
77
90
  autoscroll={gradio.autoscroll}
@@ -85,7 +98,7 @@
85
98
  {show_label}
86
99
  {row_count}
87
100
  {col_count}
88
- values={cell_values}
101
+ values={filtered_cell_values || value.data}
89
102
  {display_value}
90
103
  {styling}
91
104
  headers={_headers}
@@ -95,6 +108,7 @@
95
108
  }}
96
109
  on:input={(e) => gradio.dispatch("input")}
97
110
  on:select={(e) => gradio.dispatch("select", e.detail)}
111
+ on:search={(e) => (search_query = e.detail)}
98
112
  {wrap}
99
113
  {datatype}
100
114
  {latex_delimiters}
@@ -109,5 +123,8 @@
109
123
  {show_fullscreen_button}
110
124
  {max_chars}
111
125
  {show_copy_button}
126
+ {show_row_numbers}
127
+ {show_search}
128
+ {pinned_columns}
112
129
  />
113
130
  </Block>
@@ -4,6 +4,7 @@ export let selected = false;
4
4
  export let index;
5
5
  let hovered = false;
6
6
  let loaded = Array.isArray(value);
7
+ let is_empty = loaded && (value.length === 0 || value[0].length === 0);
7
8
  </script>
8
9
 
9
10
  {#if loaded}
@@ -18,6 +19,12 @@ let loaded = Array.isArray(value);
18
19
  >
19
20
  {#if typeof value === "string"}
20
21
  {value}
22
+ {:else if is_empty}
23
+ <table class="">
24
+ <tr>
25
+ <td>Empty</td>
26
+ </tr>
27
+ </table>
21
28
  {:else}
22
29
  <table class="">
23
30
  {#each value.slice(0, 3) as row, i}
package/dist/Index.svelte CHANGED
@@ -36,10 +36,18 @@ export let interactive;
36
36
  export let show_fullscreen_button = false;
37
37
  export let max_chars = void 0;
38
38
  export let show_copy_button = false;
39
+ export let show_row_numbers = false;
40
+ export let show_search = "none";
41
+ let search_query = null;
39
42
  $:
40
- _headers = [...value.headers || headers];
43
+ filtered_cell_values = search_query ? value.data?.filter(
44
+ (row) => row.some(
45
+ (cell) => search_query && String(cell).toLowerCase().includes(search_query.toLowerCase())
46
+ )
47
+ ) : null;
48
+ export let pinned_columns = 0;
41
49
  $:
42
- cell_values = value.data ? [...value.data] : [];
50
+ _headers = [...value.headers || headers];
43
51
  $:
44
52
  display_value = value?.metadata?.display_value ? [...value?.metadata?.display_value] : null;
45
53
  $:
@@ -54,7 +62,7 @@ $:
54
62
  container={false}
55
63
  {scale}
56
64
  {min_width}
57
- allow_overflow={false}
65
+ overflow_behavior="visible"
58
66
  >
59
67
  <StatusTracker
60
68
  autoscroll={gradio.autoscroll}
@@ -68,7 +76,7 @@ $:
68
76
  {show_label}
69
77
  {row_count}
70
78
  {col_count}
71
- values={cell_values}
79
+ values={filtered_cell_values || value.data}
72
80
  {display_value}
73
81
  {styling}
74
82
  headers={_headers}
@@ -78,6 +86,7 @@ $:
78
86
  }}
79
87
  on:input={(e) => gradio.dispatch("input")}
80
88
  on:select={(e) => gradio.dispatch("select", e.detail)}
89
+ on:search={(e) => (search_query = e.detail)}
81
90
  {wrap}
82
91
  {datatype}
83
92
  {latex_delimiters}
@@ -92,5 +101,8 @@ $:
92
101
  {show_fullscreen_button}
93
102
  {max_chars}
94
103
  {show_copy_button}
104
+ {show_row_numbers}
105
+ {show_search}
106
+ {pinned_columns}
95
107
  />
96
108
  </Block>
@@ -40,6 +40,9 @@ declare const __propDef: {
40
40
  show_fullscreen_button?: boolean | undefined;
41
41
  max_chars?: number | undefined;
42
42
  show_copy_button?: boolean | undefined;
43
+ show_row_numbers?: boolean | undefined;
44
+ show_search?: ("none" | "search" | "filter") | undefined;
45
+ pinned_columns?: number | undefined;
43
46
  };
44
47
  events: {
45
48
  [evt: string]: CustomEvent<any>;
@@ -143,4 +146,13 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
143
146
  get show_copy_button(): boolean | undefined;
144
147
  /**accessor*/
145
148
  set show_copy_button(_: boolean | undefined);
149
+ get show_row_numbers(): boolean | undefined;
150
+ /**accessor*/
151
+ set show_row_numbers(_: boolean | undefined);
152
+ get show_search(): "filter" | "none" | "search" | undefined;
153
+ /**accessor*/
154
+ set show_search(_: "filter" | "none" | "search" | undefined);
155
+ get pinned_columns(): number | undefined;
156
+ /**accessor*/
157
+ set pinned_columns(_: number | undefined);
146
158
  }
@@ -81,7 +81,7 @@ function position_menu() {
81
81
  <style>
82
82
  .cell-menu {
83
83
  position: fixed;
84
- z-index: var(--layer-2);
84
+ z-index: var(--layer-4);
85
85
  background: var(--background-fill-primary);
86
86
  border: 1px solid var(--border-color-primary);
87
87
  border-radius: var(--radius-sm);
@@ -84,7 +84,6 @@ function handle_click() {
84
84
  class:multiline={header}
85
85
  on:focus|preventDefault
86
86
  style={styling}
87
- class="table-cell-text"
88
87
  data-editable={editable}
89
88
  placeholder=" "
90
89
  >
@@ -123,7 +122,6 @@ function handle_click() {
123
122
  position: relative;
124
123
  display: inline-block;
125
124
  outline: none;
126
- padding: var(--size-2);
127
125
  -webkit-user-select: text;
128
126
  -moz-user-select: text;
129
127
  -ms-user-select: text;
@@ -133,10 +131,6 @@ function handle_click() {
133
131
  height: 100%;
134
132
  }
135
133
 
136
- input:where(:not(.header), [data-editable="true"]) {
137
- width: calc(100% - var(--size-10));
138
- }
139
-
140
134
  span.expanded {
141
135
  height: auto;
142
136
  min-height: 100%;
@@ -154,6 +148,7 @@ function handle_click() {
154
148
  font-weight: var(--weight-bold);
155
149
  white-space: normal;
156
150
  word-break: break-word;
151
+ margin-left: var(--size-1);
157
152
  }
158
153
 
159
154
  .edit {