@gradio/dataframe 0.16.5 → 0.17.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 (87) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/Dataframe.stories.svelte +202 -9
  3. package/Index.svelte +7 -13
  4. package/dist/Index.svelte +5 -9
  5. package/dist/Index.svelte.d.ts +9 -2
  6. package/dist/shared/CellMenu.svelte +91 -10
  7. package/dist/shared/CellMenu.svelte.d.ts +6 -0
  8. package/dist/shared/CellMenuButton.svelte +45 -0
  9. package/dist/shared/CellMenuButton.svelte.d.ts +16 -0
  10. package/dist/shared/CellMenuIcons.svelte +79 -0
  11. package/dist/shared/EditableCell.svelte +83 -14
  12. package/dist/shared/EditableCell.svelte.d.ts +12 -3
  13. package/dist/shared/EmptyRowButton.svelte +28 -0
  14. package/dist/shared/EmptyRowButton.svelte.d.ts +16 -0
  15. package/dist/shared/RowNumber.svelte +40 -0
  16. package/dist/shared/RowNumber.svelte.d.ts +17 -0
  17. package/dist/shared/Table.svelte +564 -1121
  18. package/dist/shared/Table.svelte.d.ts +4 -0
  19. package/dist/shared/TableCell.svelte +291 -0
  20. package/dist/shared/TableCell.svelte.d.ts +57 -0
  21. package/dist/shared/TableHeader.svelte +239 -0
  22. package/dist/shared/TableHeader.svelte.d.ts +45 -0
  23. package/dist/shared/Toolbar.svelte +18 -8
  24. package/dist/shared/VirtualTable.svelte +66 -19
  25. package/dist/shared/VirtualTable.svelte.d.ts +4 -0
  26. package/dist/shared/context/keyboard_context.d.ts +37 -0
  27. package/dist/shared/context/keyboard_context.js +12 -0
  28. package/dist/shared/context/selection_context.d.ts +32 -0
  29. package/dist/shared/context/selection_context.js +107 -0
  30. package/dist/shared/context/table_context.d.ts +141 -0
  31. package/dist/shared/context/table_context.js +375 -0
  32. package/dist/shared/icons/Padlock.svelte +24 -0
  33. package/dist/shared/icons/Padlock.svelte.d.ts +23 -0
  34. package/dist/shared/icons/SelectionButtons.svelte +85 -0
  35. package/dist/shared/icons/SelectionButtons.svelte.d.ts +18 -0
  36. package/dist/shared/icons/SortArrowDown.svelte +24 -0
  37. package/dist/shared/icons/SortArrowDown.svelte.d.ts +16 -0
  38. package/dist/shared/icons/SortArrowUp.svelte +24 -0
  39. package/dist/shared/icons/SortArrowUp.svelte.d.ts +16 -0
  40. package/dist/shared/icons/SortButtonDown.svelte +14 -0
  41. package/dist/shared/icons/SortButtonDown.svelte.d.ts +23 -0
  42. package/dist/shared/icons/SortButtonUp.svelte +15 -0
  43. package/dist/shared/icons/SortButtonUp.svelte.d.ts +23 -0
  44. package/dist/shared/icons/SortIcon.svelte +46 -68
  45. package/dist/shared/icons/SortIcon.svelte.d.ts +3 -2
  46. package/dist/shared/selection_utils.d.ts +2 -1
  47. package/dist/shared/selection_utils.js +39 -10
  48. package/dist/shared/utils/data_processing.d.ts +13 -0
  49. package/dist/shared/utils/data_processing.js +45 -0
  50. package/dist/shared/utils/drag_utils.d.ts +15 -0
  51. package/dist/shared/utils/drag_utils.js +57 -0
  52. package/dist/shared/utils/keyboard_utils.d.ts +2 -0
  53. package/dist/shared/utils/keyboard_utils.js +186 -0
  54. package/dist/shared/utils/sort_utils.d.ts +22 -3
  55. package/dist/shared/utils/sort_utils.js +44 -24
  56. package/dist/shared/utils/table_utils.d.ts +6 -5
  57. package/dist/shared/utils/table_utils.js +13 -56
  58. package/package.json +7 -7
  59. package/shared/CellMenu.svelte +90 -10
  60. package/shared/CellMenuButton.svelte +46 -0
  61. package/shared/CellMenuIcons.svelte +79 -0
  62. package/shared/EditableCell.svelte +97 -18
  63. package/shared/EmptyRowButton.svelte +29 -0
  64. package/shared/RowNumber.svelte +41 -0
  65. package/shared/Table.svelte +604 -1235
  66. package/shared/TableCell.svelte +324 -0
  67. package/shared/TableHeader.svelte +256 -0
  68. package/shared/Toolbar.svelte +19 -8
  69. package/shared/VirtualTable.svelte +72 -19
  70. package/shared/context/keyboard_context.ts +65 -0
  71. package/shared/context/selection_context.ts +168 -0
  72. package/shared/context/table_context.ts +625 -0
  73. package/shared/icons/Padlock.svelte +24 -0
  74. package/shared/icons/SelectionButtons.svelte +93 -0
  75. package/shared/icons/SortArrowDown.svelte +25 -0
  76. package/shared/icons/SortArrowUp.svelte +25 -0
  77. package/shared/icons/SortButtonDown.svelte +14 -0
  78. package/shared/icons/SortButtonUp.svelte +15 -0
  79. package/shared/icons/SortIcon.svelte +47 -70
  80. package/shared/selection_utils.ts +39 -13
  81. package/shared/utils/data_processing.ts +72 -0
  82. package/shared/utils/drag_utils.ts +92 -0
  83. package/shared/utils/keyboard_utils.ts +238 -0
  84. package/shared/utils/sort_utils.test.ts +262 -14
  85. package/shared/utils/sort_utils.ts +67 -31
  86. package/shared/utils/table_utils.test.ts +66 -45
  87. package/shared/utils/table_utils.ts +16 -86
@@ -0,0 +1,324 @@
1
+ <script lang="ts">
2
+ import EditableCell from "./EditableCell.svelte";
3
+ import CellMenuButton from "./CellMenuButton.svelte";
4
+ import type { I18nFormatter } from "js/core/src/gradio_helper";
5
+ import type { Datatype } from "./utils";
6
+ import { is_cell_in_selection } from "./selection_utils";
7
+
8
+ export let value: string | number;
9
+ export let index: number;
10
+ export let j: number;
11
+ export let actual_pinned_columns: number;
12
+ export let get_cell_width: (index: number) => string;
13
+ export let handle_cell_click: (
14
+ event: MouseEvent,
15
+ row: number,
16
+ col: number
17
+ ) => void;
18
+ export let toggle_cell_menu: (
19
+ event: MouseEvent,
20
+ row: number,
21
+ col: number
22
+ ) => void;
23
+ export let is_cell_selected: (
24
+ coords: [number, number],
25
+ selected_cells: [number, number][]
26
+ ) => string;
27
+ export let should_show_cell_menu: (
28
+ coords: [number, number],
29
+ selected_cells: [number, number][],
30
+ editable: boolean
31
+ ) => boolean;
32
+ export let selected_cells: [number, number][];
33
+ export let copy_flash: boolean;
34
+ export let active_cell_menu: {
35
+ row: number;
36
+ col: number;
37
+ x: number;
38
+ y: number;
39
+ } | null;
40
+ export let styling: string | undefined;
41
+ export let latex_delimiters: {
42
+ left: string;
43
+ right: string;
44
+ display: boolean;
45
+ }[];
46
+ export let line_breaks: boolean;
47
+ export let datatype: Datatype;
48
+ export let editing: [number, number] | false;
49
+ export let clear_on_focus: boolean;
50
+ export let max_chars: number | undefined;
51
+ export let root: string;
52
+ export let editable: boolean;
53
+ export let is_static = false;
54
+ export let i18n: I18nFormatter;
55
+ export let components: Record<string, any> = {};
56
+ export let el: {
57
+ cell: HTMLTableCellElement | null;
58
+ input: HTMLInputElement | null;
59
+ };
60
+ export let handle_select_column: (col: number) => void;
61
+ export let handle_select_row: (row: number) => void;
62
+ export let is_dragging: boolean;
63
+
64
+ function get_cell_position(col_index: number): string {
65
+ if (col_index >= actual_pinned_columns) {
66
+ return "auto";
67
+ }
68
+
69
+ if (col_index === 0) {
70
+ return "0";
71
+ }
72
+
73
+ const previous_widths = Array(col_index)
74
+ .fill(0)
75
+ .map((_, idx) => {
76
+ return get_cell_width(idx);
77
+ })
78
+ .join(" + ");
79
+
80
+ return `calc(${previous_widths})`;
81
+ }
82
+
83
+ $: cell_classes = is_cell_selected([index, j], selected_cells || []);
84
+ $: is_in_selection = is_cell_in_selection([index, j], selected_cells);
85
+ $: has_no_top = cell_classes.includes("no-top");
86
+ $: has_no_bottom = cell_classes.includes("no-bottom");
87
+ $: has_no_left = cell_classes.includes("no-left");
88
+ $: has_no_right = cell_classes.includes("no-right");
89
+ </script>
90
+
91
+ <td
92
+ class:pinned-column={j < actual_pinned_columns}
93
+ class:last-pinned={j === actual_pinned_columns - 1}
94
+ tabindex={j < actual_pinned_columns ? -1 : 0}
95
+ bind:this={el.cell}
96
+ data-row={index}
97
+ data-col={j}
98
+ data-testid={`cell-${index}-${j}`}
99
+ on:mousedown={(e) => handle_cell_click(e, index, j)}
100
+ on:contextmenu|preventDefault={(e) => toggle_cell_menu(e, index, j)}
101
+ style="width: {get_cell_width(j)}; left: {get_cell_position(j)}; {styling ||
102
+ ''}"
103
+ class:flash={copy_flash && is_in_selection}
104
+ class:cell-selected={is_in_selection}
105
+ class:no-top={has_no_top}
106
+ class:no-bottom={has_no_bottom}
107
+ class:no-left={has_no_left}
108
+ class:no-right={has_no_right}
109
+ class:menu-active={active_cell_menu &&
110
+ active_cell_menu.row === index &&
111
+ active_cell_menu.col === j}
112
+ class:dragging={is_dragging}
113
+ >
114
+ <div class="cell-wrap">
115
+ <EditableCell
116
+ bind:value
117
+ bind:el={el.input}
118
+ display_value={String(value)}
119
+ {latex_delimiters}
120
+ {line_breaks}
121
+ {editable}
122
+ {is_static}
123
+ edit={editing && editing[0] === index && editing[1] === j}
124
+ {datatype}
125
+ on:blur={() => {
126
+ clear_on_focus = false;
127
+ }}
128
+ on:focus={() => {
129
+ const row = index;
130
+ const col = j;
131
+ if (!selected_cells.some(([r, c]) => r === row && c === col)) {
132
+ selected_cells = [[row, col]];
133
+ }
134
+ }}
135
+ {clear_on_focus}
136
+ {root}
137
+ {max_chars}
138
+ {i18n}
139
+ {components}
140
+ show_selection_buttons={selected_cells.length === 1 &&
141
+ selected_cells[0][0] === index &&
142
+ selected_cells[0][1] === j}
143
+ coords={[index, j]}
144
+ on_select_column={handle_select_column}
145
+ on_select_row={handle_select_row}
146
+ {is_dragging}
147
+ />
148
+ {#if editable && should_show_cell_menu([index, j], selected_cells, editable)}
149
+ <CellMenuButton on_click={(event) => toggle_cell_menu(event, index, j)} />
150
+ {/if}
151
+ </div>
152
+ </td>
153
+
154
+ <style>
155
+ td {
156
+ --ring-color: transparent;
157
+ position: relative;
158
+ outline: none;
159
+ box-shadow: inset 0 0 0 1px var(--ring-color);
160
+ padding: 0;
161
+ border-right-width: 0px;
162
+ border-left-width: 1px;
163
+ border-style: solid;
164
+ border-color: var(--border-color-primary);
165
+ }
166
+
167
+ .cell-wrap {
168
+ display: flex;
169
+ align-items: center;
170
+ justify-content: flex-start;
171
+ outline: none;
172
+ min-height: var(--size-9);
173
+ position: relative;
174
+ height: 100%;
175
+ padding: var(--size-2);
176
+ box-sizing: border-box;
177
+ margin: 0;
178
+ gap: var(--size-1);
179
+ overflow: visible;
180
+ min-width: 0;
181
+ border-radius: var(--table-radius);
182
+ }
183
+
184
+ .cell-selected {
185
+ --ring-color: var(--color-accent);
186
+ box-shadow: inset 0 0 0 2px var(--ring-color);
187
+ z-index: 2;
188
+ position: relative;
189
+ }
190
+
191
+ .cell-selected :global(.cell-menu-button) {
192
+ display: flex;
193
+ }
194
+
195
+ .flash.cell-selected {
196
+ animation: flash-color 700ms ease-out;
197
+ }
198
+
199
+ @keyframes flash-color {
200
+ 0%,
201
+ 30% {
202
+ background: var(--color-accent-copied);
203
+ }
204
+
205
+ 100% {
206
+ background: transparent;
207
+ }
208
+ }
209
+
210
+ .pinned-column {
211
+ position: sticky;
212
+ z-index: 3;
213
+ border-right: none;
214
+ }
215
+
216
+ .pinned-column:nth-child(odd) {
217
+ background: var(--table-odd-background-fill);
218
+ }
219
+
220
+ .pinned-column:nth-child(even) {
221
+ background: var(--table-even-background-fill);
222
+ }
223
+
224
+ td:first-child {
225
+ border-left-width: 0px;
226
+ }
227
+
228
+ :global(tr:last-child) td:first-child {
229
+ border-bottom-left-radius: var(--table-radius);
230
+ }
231
+
232
+ :global(tr:last-child) td:last-child {
233
+ border-bottom-right-radius: var(--table-radius);
234
+ }
235
+
236
+ .dragging {
237
+ cursor: crosshair;
238
+ }
239
+
240
+ /* Add back the cell selection border styles */
241
+ .cell-selected.no-top {
242
+ box-shadow:
243
+ inset 2px 0 0 var(--ring-color),
244
+ inset -2px 0 0 var(--ring-color),
245
+ inset 0 -2px 0 var(--ring-color);
246
+ }
247
+
248
+ .cell-selected.no-bottom {
249
+ box-shadow:
250
+ inset 2px 0 0 var(--ring-color),
251
+ inset -2px 0 0 var(--ring-color),
252
+ inset 0 2px 0 var(--ring-color);
253
+ }
254
+
255
+ .cell-selected.no-left {
256
+ box-shadow:
257
+ inset 0 2px 0 var(--ring-color),
258
+ inset -2px 0 0 var(--ring-color),
259
+ inset 0 -2px 0 var(--ring-color);
260
+ }
261
+
262
+ .cell-selected.no-right {
263
+ box-shadow:
264
+ inset 0 2px 0 var(--ring-color),
265
+ inset 2px 0 0 var(--ring-color),
266
+ inset 0 -2px 0 var(--ring-color);
267
+ }
268
+
269
+ .cell-selected.no-top.no-left {
270
+ box-shadow:
271
+ inset -2px 0 0 var(--ring-color),
272
+ inset 0 -2px 0 var(--ring-color);
273
+ }
274
+
275
+ .cell-selected.no-top.no-right {
276
+ box-shadow:
277
+ inset 2px 0 0 var(--ring-color),
278
+ inset 0 -2px 0 var(--ring-color);
279
+ }
280
+
281
+ .cell-selected.no-bottom.no-left {
282
+ box-shadow:
283
+ inset -2px 0 0 var(--ring-color),
284
+ inset 0 2px 0 var(--ring-color);
285
+ }
286
+
287
+ .cell-selected.no-bottom.no-right {
288
+ box-shadow:
289
+ inset 2px 0 0 var(--ring-color),
290
+ inset 0 2px 0 var(--ring-color);
291
+ }
292
+
293
+ .cell-selected.no-top.no-bottom {
294
+ box-shadow:
295
+ inset 2px 0 0 var(--ring-color),
296
+ inset -2px 0 0 var(--ring-color);
297
+ }
298
+
299
+ .cell-selected.no-left.no-right {
300
+ box-shadow:
301
+ inset 0 2px 0 var(--ring-color),
302
+ inset 0 -2px 0 var(--ring-color);
303
+ }
304
+
305
+ .cell-selected.no-top.no-left.no-right {
306
+ box-shadow: inset 0 -2px 0 var(--ring-color);
307
+ }
308
+
309
+ .cell-selected.no-bottom.no-left.no-right {
310
+ box-shadow: inset 0 2px 0 var(--ring-color);
311
+ }
312
+
313
+ .cell-selected.no-left.no-top.no-bottom {
314
+ box-shadow: inset -2px 0 0 var(--ring-color);
315
+ }
316
+
317
+ .cell-selected.no-right.no-top.no-bottom {
318
+ box-shadow: inset 2px 0 0 var(--ring-color);
319
+ }
320
+
321
+ .cell-selected.no-top.no-bottom.no-left.no-right {
322
+ box-shadow: none;
323
+ }
324
+ </style>
@@ -0,0 +1,256 @@
1
+ <script lang="ts">
2
+ import EditableCell from "./EditableCell.svelte";
3
+ import CellMenuButton from "./CellMenuButton.svelte";
4
+ import type { I18nFormatter } from "js/core/src/gradio_helper";
5
+ import type { SortDirection } from "./context/table_context";
6
+ import Padlock from "./icons/Padlock.svelte";
7
+ import SortArrowUp from "./icons/SortArrowUp.svelte";
8
+ import SortArrowDown from "./icons/SortArrowDown.svelte";
9
+
10
+ export let value: string;
11
+ export let i: number;
12
+ export let actual_pinned_columns: number;
13
+ export let header_edit: number | false;
14
+ export let selected_header: number | false;
15
+ export let get_sort_status: (
16
+ value: string,
17
+ headers: string[]
18
+ ) => "none" | "asc" | "desc";
19
+ export let headers: string[];
20
+ export let get_cell_width: (index: number) => string;
21
+ export let handle_header_click: (event: MouseEvent, col: number) => void;
22
+ export let toggle_header_menu: (event: MouseEvent, col: number) => void;
23
+ export let end_header_edit: (event: CustomEvent<KeyboardEvent>) => void;
24
+ export let sort_columns: { col: number; direction: SortDirection }[] = [];
25
+
26
+ export let latex_delimiters: {
27
+ left: string;
28
+ right: string;
29
+ display: boolean;
30
+ }[];
31
+ export let line_breaks: boolean;
32
+ export let max_chars: number | undefined;
33
+ export let root: string;
34
+ export let editable: boolean;
35
+ export let i18n: I18nFormatter;
36
+ export let el: HTMLInputElement | null;
37
+ export let is_static: boolean;
38
+ export let col_count: [number, "fixed" | "dynamic"];
39
+
40
+ $: can_add_columns = col_count && col_count[1] === "dynamic";
41
+ $: sort_index = sort_columns.findIndex((item) => item.col === i);
42
+ $: sort_priority = sort_index !== -1 ? sort_index + 1 : null;
43
+ $: current_direction =
44
+ sort_index !== -1 ? sort_columns[sort_index].direction : null;
45
+
46
+ function get_header_position(col_index: number): string {
47
+ if (col_index >= actual_pinned_columns) {
48
+ return "auto";
49
+ }
50
+
51
+ if (col_index === 0) {
52
+ return "0";
53
+ }
54
+
55
+ const previous_widths = Array(col_index)
56
+ .fill(0)
57
+ .map((_, idx) => {
58
+ return get_cell_width(idx);
59
+ })
60
+ .join(" + ");
61
+
62
+ return `calc(${previous_widths})`;
63
+ }
64
+ </script>
65
+
66
+ <th
67
+ class:pinned-column={i < actual_pinned_columns}
68
+ class:last-pinned={i === actual_pinned_columns - 1}
69
+ class:focus={header_edit === i || selected_header === i}
70
+ class:sorted={sort_index !== -1}
71
+ aria-sort={get_sort_status(value, headers) === "none"
72
+ ? "none"
73
+ : get_sort_status(value, headers) === "asc"
74
+ ? "ascending"
75
+ : "descending"}
76
+ style="width: {get_cell_width(i)}; left: {get_header_position(i)};"
77
+ on:click={(event) => handle_header_click(event, i)}
78
+ on:mousedown={(event) => {
79
+ event.preventDefault();
80
+ event.stopPropagation();
81
+ }}
82
+ title={value}
83
+ >
84
+ <div class="cell-wrap">
85
+ <div class="header-content">
86
+ <button
87
+ class="header-button"
88
+ on:click={(event) => handle_header_click(event, i)}
89
+ on:mousedown={(event) => {
90
+ event.preventDefault();
91
+ event.stopPropagation();
92
+ }}
93
+ title={value}
94
+ >
95
+ <EditableCell
96
+ {max_chars}
97
+ bind:value
98
+ bind:el
99
+ {latex_delimiters}
100
+ {line_breaks}
101
+ edit={header_edit === i}
102
+ on:keydown={(event) => {
103
+ if (
104
+ event.detail.key === "Enter" ||
105
+ event.detail.key === "Escape" ||
106
+ event.detail.key === "Tab"
107
+ ) {
108
+ end_header_edit(event);
109
+ }
110
+ }}
111
+ header
112
+ {root}
113
+ {editable}
114
+ {is_static}
115
+ {i18n}
116
+ />
117
+ {#if sort_index !== -1}
118
+ <div class="sort-indicators">
119
+ <span class="sort-arrow">
120
+ {#if current_direction === "asc"}
121
+ <SortArrowUp size={12} />
122
+ {:else}
123
+ <SortArrowDown size={12} />
124
+ {/if}
125
+ </span>
126
+ {#if sort_columns.length > 1}
127
+ <span class="sort-priority">
128
+ {sort_priority}
129
+ </span>
130
+ {/if}
131
+ </div>
132
+ {/if}
133
+ </button>
134
+ {#if is_static}
135
+ <Padlock />
136
+ {/if}
137
+ </div>
138
+ {#if can_add_columns}
139
+ <CellMenuButton on_click={(event) => toggle_header_menu(event, i)} />
140
+ {/if}
141
+ </div>
142
+ </th>
143
+
144
+ <style>
145
+ th {
146
+ --ring-color: transparent;
147
+ position: relative;
148
+ outline: none;
149
+ box-shadow: inset 0 0 0 1px var(--ring-color);
150
+ padding: 0;
151
+ background: var(--table-even-background-fill);
152
+ border-right-width: 0px;
153
+ border-left-width: 1px;
154
+ border-style: solid;
155
+ border-color: var(--border-color-primary);
156
+ }
157
+
158
+ th:first-child {
159
+ border-top-left-radius: var(--table-radius);
160
+ border-bottom-left-radius: var(--table-radius);
161
+ border-left-width: 0px;
162
+ }
163
+
164
+ th:last-child {
165
+ border-top-right-radius: var(--table-radius);
166
+ border-bottom-right-radius: var(--table-radius);
167
+ }
168
+
169
+ th.focus {
170
+ --ring-color: var(--color-accent);
171
+ box-shadow: inset 0 0 0 2px var(--ring-color);
172
+ z-index: 4;
173
+ }
174
+
175
+ th.focus :global(.cell-menu-button) {
176
+ display: flex;
177
+ }
178
+
179
+ th:hover :global(.cell-menu-button) {
180
+ display: flex;
181
+ }
182
+
183
+ .cell-wrap {
184
+ display: flex;
185
+ align-items: center;
186
+ justify-content: flex-start;
187
+ outline: none;
188
+ min-height: var(--size-9);
189
+ position: relative;
190
+ height: 100%;
191
+ padding: var(--size-2);
192
+ box-sizing: border-box;
193
+ margin: 0;
194
+ gap: var(--size-1);
195
+ overflow: visible;
196
+ min-width: 0;
197
+ border-radius: var(--table-radius);
198
+ }
199
+
200
+ .header-content {
201
+ display: flex;
202
+ align-items: center;
203
+ overflow: hidden;
204
+ flex-grow: 1;
205
+ min-width: 0;
206
+ white-space: normal;
207
+ overflow-wrap: break-word;
208
+ word-break: normal;
209
+ height: 100%;
210
+ gap: var(--size-1);
211
+ }
212
+
213
+ .header-button {
214
+ display: flex;
215
+ text-align: left;
216
+ width: 100%;
217
+ overflow: hidden;
218
+ text-overflow: ellipsis;
219
+ display: flex;
220
+ align-items: center;
221
+ position: relative;
222
+ }
223
+
224
+ .sort-indicators {
225
+ display: flex;
226
+ align-items: center;
227
+ margin-left: var(--size-1);
228
+ gap: var(--size-1);
229
+ }
230
+
231
+ .sort-arrow {
232
+ display: flex;
233
+ align-items: center;
234
+ justify-content: center;
235
+ color: var(--body-text-color);
236
+ }
237
+
238
+ .sort-priority {
239
+ display: flex;
240
+ align-items: center;
241
+ justify-content: center;
242
+ font-size: var(--size-2);
243
+ background-color: var(--button-secondary-background-fill);
244
+ color: var(--body-text-color);
245
+ border-radius: var(--radius-sm);
246
+ width: var(--size-2-5);
247
+ height: var(--size-2-5);
248
+ padding: var(--size-1-5);
249
+ }
250
+
251
+ .pinned-column {
252
+ position: sticky;
253
+ z-index: 5;
254
+ border-right: none;
255
+ }
256
+ </style>
@@ -1,8 +1,7 @@
1
1
  <script lang="ts">
2
- import { Maximize, Minimize, Copy } from "@gradio/icons";
2
+ import { Maximize, Minimize, Copy, Check } from "@gradio/icons";
3
3
  import { onDestroy } from "svelte";
4
4
  import { createEventDispatcher } from "svelte";
5
- import FilterIcon from "./icons/FilterIcon.svelte";
6
5
 
7
6
  export let show_fullscreen_button = false;
8
7
  export let show_copy_button = false;
@@ -18,8 +17,17 @@
18
17
  let copied = false;
19
18
  let timer: ReturnType<typeof setTimeout>;
20
19
  export let current_search_query: string | null = null;
21
-
22
- $: dispatch("search", current_search_query);
20
+ let input_value = "";
21
+
22
+ function handle_search_input(e: Event): void {
23
+ const target = e.target as HTMLInputElement;
24
+ input_value = target.value;
25
+ const new_query = input_value || null;
26
+ if (current_search_query !== new_query) {
27
+ current_search_query = new_query;
28
+ dispatch("search", current_search_query);
29
+ }
30
+ }
23
31
 
24
32
  function copy_feedback(): void {
25
33
  copied = true;
@@ -45,9 +53,12 @@
45
53
  <div class="search-container">
46
54
  <input
47
55
  type="text"
48
- bind:value={current_search_query}
49
- placeholder="Search..."
56
+ value={current_search_query || ""}
57
+ on:input={handle_search_input}
58
+ placeholder={show_search === "filter" ? "Filter..." : "Search..."}
50
59
  class="search-input"
60
+ class:filter-mode={show_search === "filter"}
61
+ title={`Enter text to ${show_search} the table`}
51
62
  />
52
63
  {#if current_search_query && show_search === "filter"}
53
64
  <button
@@ -56,7 +67,7 @@
56
67
  aria-label="Apply filter and update dataframe values"
57
68
  title="Apply filter and update dataframe values"
58
69
  >
59
- <FilterIcon />
70
+ <Check />
60
71
  </button>
61
72
  {/if}
62
73
  </div>
@@ -69,7 +80,7 @@
69
80
  title={copied ? "Copied to clipboard" : "Copy table data"}
70
81
  >
71
82
  {#if copied}
72
- <FilterIcon />
83
+ <Check />
73
84
  {:else}
74
85
  <Copy />
75
86
  {/if}