@gradio/dataframe 0.14.0 → 0.16.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/Dataframe.stories.svelte +283 -7
  3. package/Index.svelte +22 -3
  4. package/dist/Index.svelte +18 -4
  5. package/dist/Index.svelte.d.ts +16 -0
  6. package/dist/shared/EditableCell.svelte +49 -7
  7. package/dist/shared/EditableCell.svelte.d.ts +1 -1
  8. package/dist/shared/Table.svelte +692 -411
  9. package/dist/shared/Table.svelte.d.ts +4 -0
  10. package/dist/shared/Toolbar.svelte +122 -30
  11. package/dist/shared/Toolbar.svelte.d.ts +4 -0
  12. package/dist/shared/VirtualTable.svelte +70 -26
  13. package/dist/shared/VirtualTable.svelte.d.ts +1 -0
  14. package/dist/shared/icons/FilterIcon.svelte +11 -0
  15. package/dist/shared/icons/FilterIcon.svelte.d.ts +16 -0
  16. package/dist/shared/icons/SortIcon.svelte +90 -0
  17. package/dist/shared/icons/SortIcon.svelte.d.ts +20 -0
  18. package/dist/shared/selection_utils.d.ts +30 -0
  19. package/dist/shared/selection_utils.js +139 -0
  20. package/dist/shared/types.d.ts +18 -0
  21. package/dist/shared/types.js +2 -0
  22. package/dist/shared/utils/menu_utils.d.ts +42 -0
  23. package/dist/shared/utils/menu_utils.js +58 -0
  24. package/dist/shared/utils/sort_utils.d.ts +7 -0
  25. package/dist/shared/utils/sort_utils.js +39 -0
  26. package/dist/shared/utils/table_utils.d.ts +12 -0
  27. package/dist/shared/utils/table_utils.js +148 -0
  28. package/package.json +8 -8
  29. package/shared/EditableCell.svelte +55 -7
  30. package/shared/Table.svelte +762 -478
  31. package/shared/Toolbar.svelte +125 -30
  32. package/shared/VirtualTable.svelte +73 -26
  33. package/shared/icons/FilterIcon.svelte +12 -0
  34. package/shared/icons/SortIcon.svelte +95 -0
  35. package/shared/selection_utils.ts +230 -0
  36. package/shared/types.ts +29 -0
  37. package/shared/utils/menu_utils.ts +115 -0
  38. package/shared/utils/sort_utils.test.ts +71 -0
  39. package/shared/utils/sort_utils.ts +55 -0
  40. package/shared/utils/table_utils.test.ts +114 -0
  41. package/shared/utils/table_utils.ts +206 -0
  42. package/dist/shared/table_utils.d.ts +0 -6
  43. package/dist/shared/table_utils.js +0 -27
  44. package/shared/table_utils.ts +0 -38
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # @gradio/dataframe
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Features
6
+
7
+ - [#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!
8
+ - [#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!
9
+ - [#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!
10
+ - [#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!
11
+ - [#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!
12
+ - [#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!
13
+ - [#10540](https://github.com/gradio-app/gradio/pull/10540) [`deeebfb`](https://github.com/gradio-app/gradio/commit/deeebfba46f15bb3641b86e25156215d2d727087) - Revert editable text changes. Thanks @hannahblair!
14
+
15
+ ### Fixes
16
+
17
+ - [#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!
18
+ - [#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!
19
+ - [#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!
20
+ - [#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!
21
+
22
+ ### Dependency updates
23
+
24
+ - @gradio/statustracker@0.10.3
25
+ - @gradio/atoms@0.13.2
26
+ - @gradio/utils@0.10.1
27
+ - @gradio/client@1.12.0
28
+ - @gradio/markdown-code@0.4.0
29
+ - @gradio/upload@0.15.1
30
+ - @gradio/button@0.4.6
31
+
32
+ ## 0.15.0
33
+
34
+ ### Features
35
+
36
+ - [#10456](https://github.com/gradio-app/gradio/pull/10456) [`8e40c15`](https://github.com/gradio-app/gradio/commit/8e40c15669ed1244d6f2288e55c2223279bd37a4) - Implement multiple cell selection. Thanks @hannahblair!
37
+ - [#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!
38
+ - [#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!
39
+ - [#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!
40
+ - [#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!
41
+
42
+ ### Dependency updates
43
+
44
+ - @gradio/client@1.11.0
45
+ - @gradio/upload@0.15.0
46
+ - @gradio/button@0.4.5
47
+
3
48
  ## 0.14.0
4
49
 
5
50
  ### Features
@@ -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
@@ -184,6 +204,18 @@
184
204
  }}
185
205
  />
186
206
 
207
+ <Story
208
+ name="Dataframe with link"
209
+ args={{
210
+ values: [['<a href="https://www.google.com/">google</a>']],
211
+ headers: ["link"],
212
+ datatype: ["markdown"],
213
+ interactive: false,
214
+ col_count: [1, "dynamic"],
215
+ row_count: [1, "dynamic"]
216
+ }}
217
+ />
218
+
187
219
  <Story
188
220
  name="Dataframe with dialog interactions"
189
221
  args={{
@@ -200,9 +232,11 @@
200
232
  const canvas = within(canvasElement);
201
233
 
202
234
  const cell_400 = canvas.getAllByRole("cell")[5];
203
- userEvent.click(cell_400);
235
+ await userEvent.click(cell_400);
204
236
 
205
- const open_dialog_btn = within(cell_400).getByText("");
237
+ const open_dialog_btn = await within(cell_400).findByRole("button", {
238
+ name: "⋮"
239
+ });
206
240
  await userEvent.click(open_dialog_btn);
207
241
 
208
242
  const add_row_btn = canvas.getByText("Add row above");
@@ -214,7 +248,7 @@
214
248
  />
215
249
 
216
250
  <Story
217
- name="Dataframe with fullscreen button"
251
+ name="Dataframe with fullscreen button and label and search"
218
252
  args={{
219
253
  col_count: [3, "dynamic"],
220
254
  row_count: [2, "dynamic"],
@@ -223,7 +257,51 @@
223
257
  [800, 100, 400],
224
258
  [200, 800, 700]
225
259
  ],
226
- show_fullscreen_button: true
260
+ show_fullscreen_button: true,
261
+ show_label: true,
262
+ show_copy_button: true,
263
+ show_search: "search",
264
+ label: "Test scores"
265
+ }}
266
+ />
267
+
268
+ <Story
269
+ name="Dataframe with multiple selection interactions"
270
+ args={{
271
+ values: [
272
+ [1, 2, 3, 4],
273
+ [5, 6, 7, 8],
274
+ [9, 10, 11, 12],
275
+ [13, 14, 15, 16]
276
+ ],
277
+ col_count: [4, "dynamic"],
278
+ row_count: [4, "dynamic"],
279
+ headers: ["A", "B", "C", "D"],
280
+ editable: true
281
+ }}
282
+ play={async ({ canvasElement }) => {
283
+ const canvas = within(canvasElement);
284
+ const cells = canvas.getAllByRole("cell");
285
+ const user = userEvent.setup();
286
+
287
+ // cmd+click to select non-contiguous cells
288
+ await user.keyboard("[MetaLeft>]");
289
+ await user.click(cells[4]);
290
+ await user.click(cells[6]);
291
+ await user.click(cells[2]);
292
+ await user.keyboard("[/MetaLeft]");
293
+
294
+ // shift+click to select a range
295
+ await user.keyboard("[ShiftLeft>]");
296
+ await user.click(cells[7]);
297
+ await user.click(cells[6]);
298
+ await user.keyboard("[/ShiftLeft]");
299
+
300
+ // clear selected cells
301
+ await user.keyboard("{Delete}");
302
+
303
+ // verify cells were cleared by clicking one
304
+ await user.click(cells[2]);
227
305
  }}
228
306
  />
229
307
 
@@ -232,7 +310,7 @@
232
310
  args={{
233
311
  col_count: [3, "dynamic"],
234
312
  row_count: [2, "dynamic"],
235
- headers: ["Math", "Reading", "Writifdsfsng"],
313
+ headers: ["Math", "Reading", "Writing"],
236
314
  values: [
237
315
  [800, 100, 400],
238
316
  [200, 800, 700]
@@ -244,15 +322,213 @@
244
322
  const canvas = within(canvasElement);
245
323
 
246
324
  const copy_button = canvas.getByRole("button", {
247
- name: /copy table data/i
325
+ name: "Copy table data"
248
326
  });
249
327
  await userEvent.click(copy_button);
250
328
 
251
329
  const fullscreen_button = canvas.getByRole("button", {
252
- name: /enter fullscreen/i
330
+ name: "Enter fullscreen"
253
331
  });
254
332
  await userEvent.click(fullscreen_button);
255
333
 
256
334
  await userEvent.click(fullscreen_button);
257
335
  }}
258
336
  />
337
+
338
+ <Story
339
+ name="Dataframe with row numbers"
340
+ args={{
341
+ values: [
342
+ [95, 92, 88],
343
+ [89, 90, 85],
344
+ [92, 88, 91],
345
+ [87, 85, 89],
346
+ [91, 93, 90]
347
+ ],
348
+ headers: ["Model A", "Model B", "Model C"],
349
+ label: "Model Performance",
350
+ col_count: [3, "dynamic"],
351
+ row_count: [5, "dynamic"],
352
+ show_row_numbers: true,
353
+ editable: false
354
+ }}
355
+ />
356
+
357
+ <Story
358
+ name="Dataframe with truncated text"
359
+ args={{
360
+ values: [
361
+ [
362
+ "This is a very long text that should be truncated",
363
+ "Short text",
364
+ "Another very long text that needs truncation"
365
+ ],
366
+ [
367
+ "Short",
368
+ "This text is also quite long and should be truncated as well",
369
+ "Medium length text here"
370
+ ],
371
+ [
372
+ "Medium text",
373
+ "Brief",
374
+ "This is the longest text in the entire table and it should definitely be truncated"
375
+ ]
376
+ ],
377
+ headers: ["Column A", "Column B", "Column C"],
378
+ label: "Truncated Text Example",
379
+ max_chars: 20,
380
+ col_count: [3, "dynamic"],
381
+ row_count: [3, "dynamic"]
382
+ }}
383
+ />
384
+
385
+ <Story
386
+ name="Dataframe with multiline headers"
387
+ args={{
388
+ values: [
389
+ [95, 92, 88],
390
+ [89, 90, 85],
391
+ [92, 88, 91]
392
+ ],
393
+ headers: [
394
+ "Dataset A\nAccuracy",
395
+ "Dataset B\nPrecision",
396
+ "Dataset C\nRecall"
397
+ ],
398
+ label: "Model Metrics",
399
+ col_count: [3, "dynamic"],
400
+ row_count: [3, "dynamic"],
401
+ editable: false
402
+ }}
403
+ />
404
+
405
+ <Story
406
+ name="Dataframe with row and column selection"
407
+ args={{
408
+ values: [
409
+ [1, 2, 3, 4],
410
+ [5, 6, 7, 8],
411
+ [9, 10, 11, 12],
412
+ [13, 14, 15, 16]
413
+ ],
414
+ col_count: [4, "dynamic"],
415
+ row_count: [4, "dynamic"],
416
+ headers: ["A", "B", "C", "D"],
417
+ editable: true
418
+ }}
419
+ play={async ({ canvasElement }) => {
420
+ const canvas = within(canvasElement);
421
+ const user = userEvent.setup();
422
+
423
+ const grid = canvas.getByRole("grid");
424
+ await user.click(grid);
425
+
426
+ const cells = canvas.getAllByRole("cell");
427
+ await user.click(cells[5]); // Click cell with value 6
428
+
429
+ const row_button = await canvas.findByRole("button", {
430
+ name: "Select row"
431
+ });
432
+ await user.click(row_button);
433
+
434
+ await user.click(cells[6]);
435
+
436
+ const col_button = await canvas.findByRole("button", {
437
+ name: "Select column"
438
+ });
439
+ await user.click(col_button);
440
+
441
+ await user.keyboard("{Delete}");
442
+ }}
443
+ />
444
+
445
+ <Story
446
+ name="Dataframe with lots of values"
447
+ args={{
448
+ values: [
449
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
450
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
451
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
452
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
453
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
454
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
455
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
456
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
457
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
458
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
459
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
460
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
461
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
462
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
463
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
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
+ ],
475
+ col_count: [10, "dynamic"],
476
+ row_count: [10, "dynamic"],
477
+ max_height: 700
478
+ }}
479
+ />
480
+
481
+ <Story
482
+ name="Dataframe with search and filter"
483
+ args={{
484
+ values: [
485
+ ["Cat", 5, "Pet"],
486
+ ["Horse", 3, "Farm"],
487
+ ["Snake", 1, "Pet"],
488
+ ["Cow", 4, "Farm"],
489
+ ["Dog", 6, "Pet"]
490
+ ],
491
+ headers: ["Animal", "Count", "Type"],
492
+ col_count: [3, "dynamic"],
493
+ row_count: [5, "dynamic"],
494
+ show_search: "filter",
495
+ editable: false
496
+ }}
497
+ play={async ({ canvasElement }) => {
498
+ const canvas = within(canvasElement);
499
+ const user = userEvent.setup();
500
+
501
+ const search_input = canvas.getByPlaceholderText("Search...");
502
+ await user.type(search_input, "Pet");
503
+
504
+ await new Promise((resolve) => setTimeout(resolve, 100));
505
+
506
+ const filter_button = canvas.getByLabelText(
507
+ "Apply filter and update dataframe values"
508
+ );
509
+ await user.click(filter_button);
510
+
511
+ await new Promise((resolve) => setTimeout(resolve, 100));
512
+ }}
513
+ />
514
+
515
+ <Story
516
+ name="Dataframe with frozen columns"
517
+ args={{
518
+ values: [
519
+ ["ID", "Name", "Age", "City", "Country", "Score"],
520
+ ["1", "John", "25", "New York", "USA", "95"],
521
+ ["2", "Emma", "30", "London", "UK", "88"],
522
+ ["3", "Luis", "28", "Madrid", "Spain", "92"],
523
+ ["4", "Anna", "35", "Paris", "France", "90"],
524
+ ["5", "Chen", "27", "Beijing", "China", "94"]
525
+ ],
526
+ headers: ["ID", "Name", "Age", "City", "Country", "Score"],
527
+ label: "User Data",
528
+ col_count: [6, "dynamic"],
529
+ row_count: [6, "dynamic"],
530
+ pinned_columns: 2,
531
+ show_row_numbers: true,
532
+ editable: false
533
+ }}
534
+ />
package/Index.svelte CHANGED
@@ -49,10 +49,24 @@
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;
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;
53
68
 
54
69
  $: _headers = [...(value.headers || headers)];
55
- $: cell_values = value.data ? [...value.data] : [];
56
70
  $: display_value = value?.metadata?.display_value
57
71
  ? [...value?.metadata?.display_value]
58
72
  : null;
@@ -70,7 +84,7 @@
70
84
  container={false}
71
85
  {scale}
72
86
  {min_width}
73
- allow_overflow={false}
87
+ overflow_behavior="visible"
74
88
  >
75
89
  <StatusTracker
76
90
  autoscroll={gradio.autoscroll}
@@ -84,7 +98,7 @@
84
98
  {show_label}
85
99
  {row_count}
86
100
  {col_count}
87
- values={cell_values}
101
+ values={filtered_cell_values || value.data}
88
102
  {display_value}
89
103
  {styling}
90
104
  headers={_headers}
@@ -94,6 +108,7 @@
94
108
  }}
95
109
  on:input={(e) => gradio.dispatch("input")}
96
110
  on:select={(e) => gradio.dispatch("select", e.detail)}
111
+ on:search={(e) => (search_query = e.detail)}
97
112
  {wrap}
98
113
  {datatype}
99
114
  {latex_delimiters}
@@ -106,6 +121,10 @@
106
121
  stream_handler={(...args) => gradio.client.stream(...args)}
107
122
  bind:value_is_output
108
123
  {show_fullscreen_button}
124
+ {max_chars}
109
125
  {show_copy_button}
126
+ {show_row_numbers}
127
+ {show_search}
128
+ {pinned_columns}
110
129
  />
111
130
  </Block>
package/dist/Index.svelte CHANGED
@@ -34,11 +34,20 @@ 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;
39
+ export let show_row_numbers = false;
40
+ export let show_search = "none";
41
+ let search_query = null;
38
42
  $:
39
- _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;
40
49
  $:
41
- cell_values = value.data ? [...value.data] : [];
50
+ _headers = [...value.headers || headers];
42
51
  $:
43
52
  display_value = value?.metadata?.display_value ? [...value?.metadata?.display_value] : null;
44
53
  $:
@@ -53,7 +62,7 @@ $:
53
62
  container={false}
54
63
  {scale}
55
64
  {min_width}
56
- allow_overflow={false}
65
+ overflow_behavior="visible"
57
66
  >
58
67
  <StatusTracker
59
68
  autoscroll={gradio.autoscroll}
@@ -67,7 +76,7 @@ $:
67
76
  {show_label}
68
77
  {row_count}
69
78
  {col_count}
70
- values={cell_values}
79
+ values={filtered_cell_values || value.data}
71
80
  {display_value}
72
81
  {styling}
73
82
  headers={_headers}
@@ -77,6 +86,7 @@ $:
77
86
  }}
78
87
  on:input={(e) => gradio.dispatch("input")}
79
88
  on:select={(e) => gradio.dispatch("select", e.detail)}
89
+ on:search={(e) => (search_query = e.detail)}
80
90
  {wrap}
81
91
  {datatype}
82
92
  {latex_delimiters}
@@ -89,6 +99,10 @@ $:
89
99
  stream_handler={(...args) => gradio.client.stream(...args)}
90
100
  bind:value_is_output
91
101
  {show_fullscreen_button}
102
+ {max_chars}
92
103
  {show_copy_button}
104
+ {show_row_numbers}
105
+ {show_search}
106
+ {pinned_columns}
93
107
  />
94
108
  </Block>
@@ -38,7 +38,11 @@ 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;
43
+ show_row_numbers?: boolean | undefined;
44
+ show_search?: ("none" | "search" | "filter") | undefined;
45
+ pinned_columns?: number | undefined;
42
46
  };
43
47
  events: {
44
48
  [evt: string]: CustomEvent<any>;
@@ -136,7 +140,19 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
136
140
  get show_fullscreen_button(): boolean | undefined;
137
141
  /**accessor*/
138
142
  set show_fullscreen_button(_: boolean | undefined);
143
+ get max_chars(): number | undefined;
144
+ /**accessor*/
145
+ set max_chars(_: number | undefined);
139
146
  get show_copy_button(): boolean | undefined;
140
147
  /**accessor*/
141
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);
142
158
  }
@@ -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
- value = _value;
36
- dispatch("blur");
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:dblclick
60
- tabindex="-1"
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 value}
92
+ {@html display_text}
70
93
  {:else if datatype === "markdown"}
71
94
  <MarkdownCode
72
- message={value.toLocaleString()}
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 ? value : display_value || value}
102
+ {editable ? display_text : display_value || display_text}
80
103
  {/if}
81
104
  </span>
82
105
 
@@ -97,18 +120,37 @@ 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);
127
+ padding-right: 0;
102
128
  -webkit-user-select: text;
103
129
  -moz-user-select: text;
104
130
  -ms-user-select: text;
105
131
  user-select: text;
106
132
  cursor: text;
133
+ width: 100%;
134
+ height: 100%;
135
+ }
136
+
137
+ span.expanded {
138
+ height: auto;
139
+ min-height: 100%;
140
+ white-space: pre-wrap;
141
+ word-break: break-word;
142
+ white-space: normal;
143
+ }
144
+
145
+ .multiline {
146
+ white-space: pre-line;
107
147
  }
108
148
 
109
149
  .header {
110
150
  transform: translateX(0);
111
151
  font-weight: var(--weight-bold);
152
+ white-space: normal;
153
+ word-break: break-word;
112
154
  }
113
155
 
114
156
  .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>;