@gradio/dataframe 0.17.17 → 0.18.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.
- package/CHANGELOG.md +32 -0
- package/Dataframe.stories.svelte +1 -2
- package/Index.svelte +3 -13
- package/dist/Index.svelte +3 -10
- package/dist/Index.svelte.d.ts +1 -5
- package/dist/shared/CellMenu.svelte +37 -0
- package/dist/shared/CellMenu.svelte.d.ts +4 -1
- package/dist/shared/CellMenuIcons.svelte +48 -0
- package/dist/shared/EditableCell.svelte +16 -20
- package/dist/shared/EditableCell.svelte.d.ts +1 -2
- package/dist/shared/FilterMenu.svelte +235 -0
- package/dist/shared/FilterMenu.svelte.d.ts +19 -0
- package/dist/shared/Table.svelte +59 -1
- package/dist/shared/TableCell.svelte.d.ts +1 -1
- package/dist/shared/TableHeader.svelte +26 -0
- package/dist/shared/TableHeader.svelte.d.ts +8 -1
- package/dist/shared/context/dataframe_context.d.ts +20 -1
- package/dist/shared/context/dataframe_context.js +46 -6
- package/dist/shared/types.d.ts +1 -1
- package/dist/shared/utils/data_processing.d.ts +2 -2
- package/dist/shared/utils/filter_utils.d.ts +28 -0
- package/dist/shared/utils/filter_utils.js +123 -0
- package/dist/shared/utils/keyboard_utils.js +5 -1
- package/dist/shared/utils/table_utils.d.ts +7 -0
- package/dist/shared/utils/table_utils.js +29 -0
- package/package.json +10 -10
- package/shared/CellMenu.svelte +45 -1
- package/shared/CellMenuIcons.svelte +48 -0
- package/shared/EditableCell.svelte +18 -25
- package/shared/FilterMenu.svelte +248 -0
- package/shared/Table.svelte +79 -5
- package/shared/TableCell.svelte +1 -1
- package/shared/TableHeader.svelte +32 -1
- package/shared/Toolbar.svelte +1 -1
- package/shared/context/dataframe_context.ts +81 -18
- package/shared/types.ts +1 -1
- package/shared/utils/data_processing.ts +2 -2
- package/shared/utils/filter_utils.ts +207 -0
- package/shared/utils/keyboard_utils.ts +5 -2
- package/shared/utils/table_utils.ts +52 -0
package/dist/shared/Table.svelte
CHANGED
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
create_drag_handlers
|
|
34
34
|
} from "./utils/drag_utils";
|
|
35
35
|
import { sort_data_and_preserve_selection } from "./utils/sort_utils";
|
|
36
|
+
import { filter_data_and_preserve_selection } from "./utils/filter_utils";
|
|
36
37
|
export let datatype;
|
|
37
38
|
export let label = null;
|
|
38
39
|
export let show_label = true;
|
|
@@ -198,6 +199,11 @@ $:
|
|
|
198
199
|
df_actions.handle_sort(-1, "asc");
|
|
199
200
|
df_actions.reset_sort_state();
|
|
200
201
|
}
|
|
202
|
+
if ($df_state.filter_state.filter_columns.length > 0) {
|
|
203
|
+
filter_data(data, display_value, styling);
|
|
204
|
+
} else {
|
|
205
|
+
df_actions.reset_filter_state();
|
|
206
|
+
}
|
|
201
207
|
if ($df_state.current_search_query) {
|
|
202
208
|
df_actions.handle_search(null);
|
|
203
209
|
}
|
|
@@ -259,12 +265,25 @@ function handle_sort(col, direction) {
|
|
|
259
265
|
}
|
|
260
266
|
function clear_sort() {
|
|
261
267
|
df_actions.reset_sort_state();
|
|
268
|
+
sort_data(data, display_value, styling);
|
|
262
269
|
}
|
|
263
|
-
$:
|
|
270
|
+
$: {
|
|
271
|
+
if ($df_state.filter_state.filter_columns.length > 0) {
|
|
272
|
+
filter_data(data, display_value, styling);
|
|
273
|
+
}
|
|
264
274
|
if ($df_state.sort_state.sort_columns.length > 0) {
|
|
265
275
|
sort_data(data, display_value, styling);
|
|
266
276
|
df_actions.update_row_order(data);
|
|
267
277
|
}
|
|
278
|
+
}
|
|
279
|
+
function handle_filter(col, datatype2, filter, value) {
|
|
280
|
+
df_actions.handle_filter(col, datatype2, filter, value);
|
|
281
|
+
filter_data(data, display_value, styling);
|
|
282
|
+
}
|
|
283
|
+
function clear_filter() {
|
|
284
|
+
df_actions.reset_filter_state();
|
|
285
|
+
filter_data(data, display_value, styling);
|
|
286
|
+
}
|
|
268
287
|
async function edit_header(i, _select = false) {
|
|
269
288
|
if (!editable || header_edit === i || col_count[1] !== "dynamic")
|
|
270
289
|
return;
|
|
@@ -355,6 +374,9 @@ let last_width_data_length = 0;
|
|
|
355
374
|
let last_width_column_count = 0;
|
|
356
375
|
function set_cell_widths() {
|
|
357
376
|
const column_count = data[0]?.length || 0;
|
|
377
|
+
if ($df_state.filter_state.filter_columns.length > 0) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
358
380
|
if (last_width_data_length === data.length && last_width_column_count === column_count && $df_state.sort_state.sort_columns.length > 0) {
|
|
359
381
|
return;
|
|
360
382
|
}
|
|
@@ -401,6 +423,21 @@ function sort_data(_data, _display_value, _styling) {
|
|
|
401
423
|
data = result.data;
|
|
402
424
|
selected = result.selected;
|
|
403
425
|
}
|
|
426
|
+
function filter_data(_data, _display_value, _styling) {
|
|
427
|
+
const result = filter_data_and_preserve_selection(
|
|
428
|
+
_data,
|
|
429
|
+
_display_value,
|
|
430
|
+
_styling,
|
|
431
|
+
$df_state.filter_state.filter_columns,
|
|
432
|
+
selected,
|
|
433
|
+
get_current_indices,
|
|
434
|
+
$df_state.filter_state.initial_data?.data,
|
|
435
|
+
$df_state.filter_state.initial_data?.display_value,
|
|
436
|
+
$df_state.filter_state.initial_data?.styling
|
|
437
|
+
);
|
|
438
|
+
data = result.data;
|
|
439
|
+
selected = result.selected;
|
|
440
|
+
}
|
|
404
441
|
$:
|
|
405
442
|
selected_index = !!selected && selected[0];
|
|
406
443
|
let is_visible = false;
|
|
@@ -656,6 +693,7 @@ function get_cell_display_value(row, col) {
|
|
|
656
693
|
{toggle_header_menu}
|
|
657
694
|
{end_header_edit}
|
|
658
695
|
sort_columns={$df_state.sort_state.sort_columns}
|
|
696
|
+
filter_columns={$df_state.filter_state.filter_columns}
|
|
659
697
|
{latex_delimiters}
|
|
660
698
|
{line_breaks}
|
|
661
699
|
{max_chars}
|
|
@@ -760,6 +798,7 @@ function get_cell_display_value(row, col) {
|
|
|
760
798
|
{toggle_header_menu}
|
|
761
799
|
{end_header_edit}
|
|
762
800
|
sort_columns={$df_state.sort_state.sort_columns}
|
|
801
|
+
filter_columns={$df_state.filter_state.filter_columns}
|
|
763
802
|
{latex_delimiters}
|
|
764
803
|
{line_breaks}
|
|
765
804
|
{max_chars}
|
|
@@ -876,6 +915,25 @@ function get_cell_display_value(row, col) {
|
|
|
876
915
|
(item) => item.col === (active_header_menu?.col ?? -1)
|
|
877
916
|
) + 1 || null
|
|
878
917
|
: null}
|
|
918
|
+
on_filter={active_header_menu
|
|
919
|
+
? (datatype, filter, value) => {
|
|
920
|
+
if (active_header_menu) {
|
|
921
|
+
handle_filter(active_header_menu.col, datatype, filter, value);
|
|
922
|
+
df_actions.set_active_header_menu(null);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
: undefined}
|
|
926
|
+
on_clear_filter={active_header_menu
|
|
927
|
+
? () => {
|
|
928
|
+
clear_filter();
|
|
929
|
+
df_actions.set_active_header_menu(null);
|
|
930
|
+
}
|
|
931
|
+
: undefined}
|
|
932
|
+
filter_active={active_header_menu
|
|
933
|
+
? $df_state.filter_state.filter_columns.some(
|
|
934
|
+
(c) => c.col === (active_header_menu?.col ?? -1)
|
|
935
|
+
)
|
|
936
|
+
: null}
|
|
879
937
|
/>
|
|
880
938
|
{/if}
|
|
881
939
|
|
|
@@ -40,7 +40,7 @@ declare const __propDef: {
|
|
|
40
40
|
components?: Record<string, any> | undefined;
|
|
41
41
|
el: {
|
|
42
42
|
cell: HTMLTableCellElement | null;
|
|
43
|
-
input:
|
|
43
|
+
input: HTMLTextAreaElement | null;
|
|
44
44
|
};
|
|
45
45
|
handle_select_column: (col: number) => void;
|
|
46
46
|
handle_select_row: (row: number) => void;
|
|
@@ -4,6 +4,7 @@ import { get_sort_status } from "./utils/sort_utils";
|
|
|
4
4
|
import Padlock from "./icons/Padlock.svelte";
|
|
5
5
|
import SortArrowUp from "./icons/SortArrowUp.svelte";
|
|
6
6
|
import SortArrowDown from "./icons/SortArrowDown.svelte";
|
|
7
|
+
import CellMenuIcons from "./CellMenuIcons.svelte";
|
|
7
8
|
export let value;
|
|
8
9
|
export let i;
|
|
9
10
|
export let actual_pinned_columns;
|
|
@@ -15,6 +16,7 @@ export let handle_header_click;
|
|
|
15
16
|
export let toggle_header_menu;
|
|
16
17
|
export let end_header_edit;
|
|
17
18
|
export let sort_columns = [];
|
|
19
|
+
export let filter_columns = [];
|
|
18
20
|
export let latex_delimiters;
|
|
19
21
|
export let line_breaks;
|
|
20
22
|
export let max_chars;
|
|
@@ -27,6 +29,8 @@ $:
|
|
|
27
29
|
can_add_columns = col_count && col_count[1] === "dynamic";
|
|
28
30
|
$:
|
|
29
31
|
sort_index = sort_columns.findIndex((item) => item.col === i);
|
|
32
|
+
$:
|
|
33
|
+
filter_index = filter_columns.findIndex((item) => item.col === i);
|
|
30
34
|
$:
|
|
31
35
|
sort_priority = sort_index !== -1 ? sort_index + 1 : null;
|
|
32
36
|
$:
|
|
@@ -50,6 +54,7 @@ function get_header_position(col_index) {
|
|
|
50
54
|
class:last-pinned={i === actual_pinned_columns - 1}
|
|
51
55
|
class:focus={header_edit === i || selected_header === i}
|
|
52
56
|
class:sorted={sort_index !== -1}
|
|
57
|
+
class:filtered={filter_index !== -1}
|
|
53
58
|
aria-sort={get_sort_status(value, sort_columns, headers) === "none"
|
|
54
59
|
? "none"
|
|
55
60
|
: get_sort_status(value, sort_columns, headers) === "asc"
|
|
@@ -112,6 +117,13 @@ function get_header_position(col_index) {
|
|
|
112
117
|
{/if}
|
|
113
118
|
</div>
|
|
114
119
|
{/if}
|
|
120
|
+
{#if filter_index !== -1}
|
|
121
|
+
<div class="filter-indicators">
|
|
122
|
+
<span class="filter-icon">
|
|
123
|
+
<CellMenuIcons icon="filter" />
|
|
124
|
+
</span>
|
|
125
|
+
</div>
|
|
126
|
+
{/if}
|
|
115
127
|
</button>
|
|
116
128
|
{#if is_static}
|
|
117
129
|
<Padlock />
|
|
@@ -230,6 +242,20 @@ function get_header_position(col_index) {
|
|
|
230
242
|
padding: var(--size-1-5);
|
|
231
243
|
}
|
|
232
244
|
|
|
245
|
+
.filter-indicators {
|
|
246
|
+
display: flex;
|
|
247
|
+
align-items: center;
|
|
248
|
+
margin-left: var(--size-1);
|
|
249
|
+
gap: var(--size-1);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.filter-icon {
|
|
253
|
+
display: flex;
|
|
254
|
+
align-items: center;
|
|
255
|
+
justify-content: center;
|
|
256
|
+
color: var(--body-text-color);
|
|
257
|
+
}
|
|
258
|
+
|
|
233
259
|
.pinned-column {
|
|
234
260
|
position: sticky;
|
|
235
261
|
z-index: 5;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
3
3
|
import type { SortDirection } from "./context/dataframe_context";
|
|
4
|
+
import type { FilterDatatype } from "./context/dataframe_context";
|
|
4
5
|
declare const __propDef: {
|
|
5
6
|
props: {
|
|
6
7
|
value: string;
|
|
@@ -17,6 +18,12 @@ declare const __propDef: {
|
|
|
17
18
|
col: number;
|
|
18
19
|
direction: SortDirection;
|
|
19
20
|
}[] | undefined;
|
|
21
|
+
filter_columns?: {
|
|
22
|
+
col: number;
|
|
23
|
+
datatype: FilterDatatype;
|
|
24
|
+
filter: string;
|
|
25
|
+
value: string;
|
|
26
|
+
}[] | undefined;
|
|
20
27
|
latex_delimiters: {
|
|
21
28
|
left: string;
|
|
22
29
|
right: string;
|
|
@@ -26,7 +33,7 @@ declare const __propDef: {
|
|
|
26
33
|
max_chars: number | undefined;
|
|
27
34
|
editable: boolean;
|
|
28
35
|
i18n: I18nFormatter;
|
|
29
|
-
el:
|
|
36
|
+
el: HTMLTextAreaElement | null;
|
|
30
37
|
is_static: boolean;
|
|
31
38
|
col_count: [number, "fixed" | "dynamic"];
|
|
32
39
|
};
|
|
@@ -2,6 +2,7 @@ import { writable } from "svelte/store";
|
|
|
2
2
|
import { get_next_cell_coordinates, get_range_selection, move_cursor } from "../selection_utils";
|
|
3
3
|
export declare const DATAFRAME_KEY: unique symbol;
|
|
4
4
|
export type SortDirection = "asc" | "desc";
|
|
5
|
+
export type FilterDatatype = "string" | "number";
|
|
5
6
|
export type CellCoordinate = [number, number];
|
|
6
7
|
interface DataFrameState {
|
|
7
8
|
config: {
|
|
@@ -34,6 +35,22 @@ interface DataFrameState {
|
|
|
34
35
|
styling: string[][] | null;
|
|
35
36
|
} | null;
|
|
36
37
|
};
|
|
38
|
+
filter_state: {
|
|
39
|
+
filter_columns: {
|
|
40
|
+
col: number;
|
|
41
|
+
datatype: FilterDatatype;
|
|
42
|
+
filter: string;
|
|
43
|
+
value: string;
|
|
44
|
+
}[];
|
|
45
|
+
initial_data: {
|
|
46
|
+
data: {
|
|
47
|
+
id: string;
|
|
48
|
+
value: string | number;
|
|
49
|
+
}[][];
|
|
50
|
+
display_value: string[][] | null;
|
|
51
|
+
styling: string[][] | null;
|
|
52
|
+
} | null;
|
|
53
|
+
};
|
|
37
54
|
ui_state: {
|
|
38
55
|
active_cell_menu: {
|
|
39
56
|
row: number;
|
|
@@ -62,6 +79,7 @@ interface DataFrameState {
|
|
|
62
79
|
interface DataFrameActions {
|
|
63
80
|
handle_search: (query: string | null) => void;
|
|
64
81
|
handle_sort: (col: number, direction: SortDirection) => void;
|
|
82
|
+
handle_filter: (col: number, datatype: FilterDatatype, filter: string, value: string) => void;
|
|
65
83
|
get_sort_status: (name: string, headers: string[]) => "none" | "asc" | "desc";
|
|
66
84
|
sort_data: (data: any[][], display_value: string[][] | null, styling: string[][] | null) => void;
|
|
67
85
|
update_row_order: (data: any[][]) => void;
|
|
@@ -88,6 +106,7 @@ interface DataFrameActions {
|
|
|
88
106
|
};
|
|
89
107
|
trigger_change: (data: any[][], headers: any[], previous_data: string[][], previous_headers: string[], value_is_output: boolean, dispatch: (e: "change" | "input", detail?: any) => void) => Promise<void>;
|
|
90
108
|
reset_sort_state: () => void;
|
|
109
|
+
reset_filter_state: () => void;
|
|
91
110
|
set_active_cell_menu: (menu: {
|
|
92
111
|
row: number;
|
|
93
112
|
col: number;
|
|
@@ -146,7 +165,7 @@ export interface DataFrameContext {
|
|
|
146
165
|
styling?: string[][] | null;
|
|
147
166
|
els?: Record<string, {
|
|
148
167
|
cell: HTMLTableCellElement | null;
|
|
149
|
-
input:
|
|
168
|
+
input: HTMLTextAreaElement | null;
|
|
150
169
|
}>;
|
|
151
170
|
parent_element?: HTMLElement;
|
|
152
171
|
get_data_at?: (row: number, col: number) => string | number;
|
|
@@ -30,6 +30,11 @@ function create_actions(state, context) {
|
|
|
30
30
|
}
|
|
31
31
|
return { data: new_data, headers: new_headers };
|
|
32
32
|
};
|
|
33
|
+
const update_array = (source, target) => {
|
|
34
|
+
if (source && target) {
|
|
35
|
+
target.splice(0, target.length, ...JSON.parse(JSON.stringify(source)));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
33
38
|
return {
|
|
34
39
|
handle_search: (query) => update_state((s) => ({ current_search_query: query })),
|
|
35
40
|
handle_sort: (col, direction) => update_state((s) => {
|
|
@@ -57,6 +62,33 @@ function create_actions(state, context) {
|
|
|
57
62
|
}
|
|
58
63
|
};
|
|
59
64
|
}),
|
|
65
|
+
handle_filter: (col, datatype, filter, value) => update_state((s) => {
|
|
66
|
+
const filter_cols = s.filter_state.filter_columns.some((c) => c.col === col)
|
|
67
|
+
? s.filter_state.filter_columns.filter((c) => c.col !== col)
|
|
68
|
+
: [
|
|
69
|
+
...s.filter_state.filter_columns,
|
|
70
|
+
{ col, datatype, filter, value }
|
|
71
|
+
];
|
|
72
|
+
const initial_data = s.filter_state.initial_data ||
|
|
73
|
+
(context.data && filter_cols.length > 0
|
|
74
|
+
? {
|
|
75
|
+
data: JSON.parse(JSON.stringify(context.data)),
|
|
76
|
+
display_value: context.display_value
|
|
77
|
+
? JSON.parse(JSON.stringify(context.display_value))
|
|
78
|
+
: null,
|
|
79
|
+
styling: context.styling
|
|
80
|
+
? JSON.parse(JSON.stringify(context.styling))
|
|
81
|
+
: null
|
|
82
|
+
}
|
|
83
|
+
: null);
|
|
84
|
+
return {
|
|
85
|
+
filter_state: {
|
|
86
|
+
...s.filter_state,
|
|
87
|
+
filter_columns: filter_cols,
|
|
88
|
+
initial_data: initial_data
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}),
|
|
60
92
|
get_sort_status: (name, headers) => {
|
|
61
93
|
const s = get(state);
|
|
62
94
|
const sort_item = s.sort_state.sort_columns.find((item) => headers[item.col] === name);
|
|
@@ -127,7 +159,8 @@ function create_actions(state, context) {
|
|
|
127
159
|
!dequal(current_headers, previous_headers)) {
|
|
128
160
|
if (!dequal(current_headers, previous_headers)) {
|
|
129
161
|
update_state((s) => ({
|
|
130
|
-
sort_state: { sort_columns: [], row_order: [], initial_data: null }
|
|
162
|
+
sort_state: { sort_columns: [], row_order: [], initial_data: null },
|
|
163
|
+
filter_state: { filter_columns: [], initial_data: null }
|
|
131
164
|
}));
|
|
132
165
|
}
|
|
133
166
|
dispatch("change", {
|
|
@@ -142,11 +175,6 @@ function create_actions(state, context) {
|
|
|
142
175
|
reset_sort_state: () => update_state((s) => {
|
|
143
176
|
if (s.sort_state.initial_data && context.data) {
|
|
144
177
|
const original = s.sort_state.initial_data;
|
|
145
|
-
const update_array = (source, target) => {
|
|
146
|
-
if (source && target) {
|
|
147
|
-
target.splice(0, target.length, ...JSON.parse(JSON.stringify(source)));
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
178
|
update_array(original.data, context.data);
|
|
151
179
|
update_array(original.display_value, context.display_value);
|
|
152
180
|
update_array(original.styling, context.styling);
|
|
@@ -155,6 +183,17 @@ function create_actions(state, context) {
|
|
|
155
183
|
sort_state: { sort_columns: [], row_order: [], initial_data: null }
|
|
156
184
|
};
|
|
157
185
|
}),
|
|
186
|
+
reset_filter_state: () => update_state((s) => {
|
|
187
|
+
if (s.filter_state.initial_data && context.data) {
|
|
188
|
+
const original = s.filter_state.initial_data;
|
|
189
|
+
update_array(original.data, context.data);
|
|
190
|
+
update_array(original.display_value, context.display_value);
|
|
191
|
+
update_array(original.styling, context.styling);
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
filter_state: { filter_columns: [], initial_data: null }
|
|
195
|
+
};
|
|
196
|
+
}),
|
|
158
197
|
set_active_cell_menu: (menu) => update_state((s) => ({
|
|
159
198
|
ui_state: { ...s.ui_state, active_cell_menu: menu }
|
|
160
199
|
})),
|
|
@@ -341,6 +380,7 @@ export function create_dataframe_context(config) {
|
|
|
341
380
|
config,
|
|
342
381
|
current_search_query: null,
|
|
343
382
|
sort_state: { sort_columns: [], row_order: [], initial_data: null },
|
|
383
|
+
filter_state: { filter_columns: [], initial_data: null },
|
|
344
384
|
ui_state: {
|
|
345
385
|
active_cell_menu: null,
|
|
346
386
|
active_header_menu: null,
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -13,6 +13,6 @@ export type TableData = TableCell[][];
|
|
|
13
13
|
export type CountConfig = [number, "fixed" | "dynamic"];
|
|
14
14
|
export type ElementRefs = Record<string, {
|
|
15
15
|
cell: null | HTMLTableCellElement;
|
|
16
|
-
input: null |
|
|
16
|
+
input: null | HTMLTextAreaElement;
|
|
17
17
|
}>;
|
|
18
18
|
export type DataBinding = Record<string, TableCell>;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Headers, HeadersWithIDs } from "../utils";
|
|
2
2
|
export declare function make_headers(_head: Headers, col_count: [number, "fixed" | "dynamic"], els: Record<string, {
|
|
3
3
|
cell: null | HTMLTableCellElement;
|
|
4
|
-
input: null |
|
|
4
|
+
input: null | HTMLTextAreaElement;
|
|
5
5
|
}>, make_id: () => string): HeadersWithIDs;
|
|
6
6
|
export declare function process_data(values: (string | number)[][], els: Record<string, {
|
|
7
7
|
cell: null | HTMLTableCellElement;
|
|
8
|
-
input: null |
|
|
8
|
+
input: null | HTMLTextAreaElement;
|
|
9
9
|
}>, data_binding: Record<string, any>, make_id: () => string, display_value?: string[][] | null): {
|
|
10
10
|
id: string;
|
|
11
11
|
value: string | number;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type FilterDatatype = "string" | "number";
|
|
2
|
+
export declare function filter_data(data: {
|
|
3
|
+
id: string;
|
|
4
|
+
value: string | number;
|
|
5
|
+
}[][], filter_columns: {
|
|
6
|
+
col: number;
|
|
7
|
+
datatype: FilterDatatype;
|
|
8
|
+
filter: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}[]): number[];
|
|
11
|
+
export declare function filter_data_and_preserve_selection(data: {
|
|
12
|
+
id: string;
|
|
13
|
+
value: string | number;
|
|
14
|
+
}[][], display_value: string[][] | null, styling: string[][] | null, filter_columns: {
|
|
15
|
+
col: number;
|
|
16
|
+
datatype: FilterDatatype;
|
|
17
|
+
filter: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}[], selected: [number, number] | false, get_current_indices: (id: string, data: {
|
|
20
|
+
id: string;
|
|
21
|
+
value: string | number;
|
|
22
|
+
}[][]) => [number, number], original_data?: {
|
|
23
|
+
id: string;
|
|
24
|
+
value: string | number;
|
|
25
|
+
}[][], original_display_value?: string[][] | null, original_styling?: string[][] | null): {
|
|
26
|
+
data: typeof data;
|
|
27
|
+
selected: [number, number] | false;
|
|
28
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { filter_table_data } from "./table_utils";
|
|
2
|
+
export function filter_data(data, filter_columns) {
|
|
3
|
+
if (!data || !data.length || !data[0]) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
let row_indices = [...Array(data.length)].map((_, i) => i);
|
|
7
|
+
if (filter_columns.length > 0) {
|
|
8
|
+
filter_columns.forEach((column) => {
|
|
9
|
+
if (column.datatype === "string") {
|
|
10
|
+
switch (column.filter) {
|
|
11
|
+
case "Contains":
|
|
12
|
+
row_indices = row_indices.filter((i) => data[i][column.col]?.value.toString().includes(column.value));
|
|
13
|
+
break;
|
|
14
|
+
case "Does not contain":
|
|
15
|
+
row_indices = row_indices.filter((i) => !data[i][column.col]?.value.toString().includes(column.value));
|
|
16
|
+
break;
|
|
17
|
+
case "Starts with":
|
|
18
|
+
row_indices = row_indices.filter((i) => data[i][column.col]?.value.toString().startsWith(column.value));
|
|
19
|
+
break;
|
|
20
|
+
case "Ends with":
|
|
21
|
+
row_indices = row_indices.filter((i) => data[i][column.col]?.value.toString().endsWith(column.value));
|
|
22
|
+
break;
|
|
23
|
+
case "Is":
|
|
24
|
+
row_indices = row_indices.filter((i) => data[i][column.col]?.value.toString() === column.value);
|
|
25
|
+
break;
|
|
26
|
+
case "Is not":
|
|
27
|
+
row_indices = row_indices.filter((i) => !(data[i][column.col]?.value.toString() === column.value));
|
|
28
|
+
break;
|
|
29
|
+
case "Is empty":
|
|
30
|
+
row_indices = row_indices.filter((i) => data[i][column.col]?.value.toString() === "");
|
|
31
|
+
break;
|
|
32
|
+
case "Is not empty":
|
|
33
|
+
row_indices = row_indices.filter((i) => !(data[i][column.col]?.value.toString() === ""));
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (column.datatype === "number") {
|
|
38
|
+
switch (column.filter) {
|
|
39
|
+
case "=":
|
|
40
|
+
row_indices = row_indices.filter((i) => {
|
|
41
|
+
if (!isNaN(Number(data[i][column.col]?.value)) &&
|
|
42
|
+
!isNaN(Number(column.value))) {
|
|
43
|
+
return (Number(data[i][column.col]?.value) === Number(column.value));
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
break;
|
|
48
|
+
case "≠":
|
|
49
|
+
row_indices = row_indices.filter((i) => {
|
|
50
|
+
if (!isNaN(Number(data[i][column.col]?.value)) &&
|
|
51
|
+
!isNaN(Number(column.value))) {
|
|
52
|
+
return !(Number(data[i][column.col]?.value) === Number(column.value));
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
});
|
|
56
|
+
break;
|
|
57
|
+
case ">":
|
|
58
|
+
row_indices = row_indices.filter((i) => {
|
|
59
|
+
if (!isNaN(Number(data[i][column.col]?.value)) &&
|
|
60
|
+
!isNaN(Number(column.value))) {
|
|
61
|
+
return (Number(data[i][column.col]?.value) > Number(column.value));
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
case "<":
|
|
67
|
+
row_indices = row_indices.filter((i) => {
|
|
68
|
+
if (!isNaN(Number(data[i][column.col]?.value)) &&
|
|
69
|
+
!isNaN(Number(column.value))) {
|
|
70
|
+
return (Number(data[i][column.col]?.value) < Number(column.value));
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
});
|
|
74
|
+
break;
|
|
75
|
+
case "≥":
|
|
76
|
+
row_indices = row_indices.filter((i) => {
|
|
77
|
+
if (!isNaN(Number(data[i][column.col]?.value)) &&
|
|
78
|
+
!isNaN(Number(column.value))) {
|
|
79
|
+
return (Number(data[i][column.col]?.value) >= Number(column.value));
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
});
|
|
83
|
+
break;
|
|
84
|
+
case "≤":
|
|
85
|
+
row_indices = row_indices.filter((i) => {
|
|
86
|
+
if (!isNaN(Number(data[i][column.col]?.value)) &&
|
|
87
|
+
!isNaN(Number(column.value))) {
|
|
88
|
+
return (Number(data[i][column.col]?.value) <= Number(column.value));
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
});
|
|
92
|
+
break;
|
|
93
|
+
case "Is empty":
|
|
94
|
+
row_indices = row_indices.filter((i) => data[i][column.col]?.value.toString() === "");
|
|
95
|
+
break;
|
|
96
|
+
case "Is not empty":
|
|
97
|
+
row_indices = row_indices.filter((i) => {
|
|
98
|
+
if (!isNaN(Number(data[i][column.col]?.value))) {
|
|
99
|
+
return !(data[i][column.col]?.value.toString() === "");
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
});
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return row_indices;
|
|
108
|
+
}
|
|
109
|
+
return [...Array(data.length)].map((_, i) => i);
|
|
110
|
+
}
|
|
111
|
+
export function filter_data_and_preserve_selection(data, display_value, styling, filter_columns, selected, get_current_indices, original_data, original_display_value, original_styling) {
|
|
112
|
+
let id = null;
|
|
113
|
+
if (selected && selected[0] in data && selected[1] in data[selected[0]]) {
|
|
114
|
+
id = data[selected[0]][selected[1]].id;
|
|
115
|
+
}
|
|
116
|
+
filter_table_data(data, display_value, styling, filter_columns, original_data, original_display_value, original_styling);
|
|
117
|
+
let new_selected = selected;
|
|
118
|
+
if (id) {
|
|
119
|
+
const [i, j] = get_current_indices(id, data);
|
|
120
|
+
new_selected = [i, j];
|
|
121
|
+
}
|
|
122
|
+
return { data, selected: new_selected };
|
|
123
|
+
}
|
|
@@ -129,8 +129,10 @@ async function handle_enter_key(event, ctx, i, j) {
|
|
|
129
129
|
const state = get(ctx.state);
|
|
130
130
|
if (!state.config.editable)
|
|
131
131
|
return false;
|
|
132
|
-
event.preventDefault();
|
|
133
132
|
const editing = state.ui_state.editing;
|
|
133
|
+
if (editing && event.shiftKey)
|
|
134
|
+
return false;
|
|
135
|
+
event.preventDefault();
|
|
134
136
|
if (editing && dequal(editing, [i, j])) {
|
|
135
137
|
const cell_id = ctx.data[i][j].id;
|
|
136
138
|
const input_el = ctx.els[cell_id]?.input;
|
|
@@ -138,6 +140,8 @@ async function handle_enter_key(event, ctx, i, j) {
|
|
|
138
140
|
await save_cell_value(input_el.value, ctx, i, j);
|
|
139
141
|
}
|
|
140
142
|
ctx.actions.set_editing(false);
|
|
143
|
+
await tick();
|
|
144
|
+
ctx.parent_element?.focus();
|
|
141
145
|
}
|
|
142
146
|
else {
|
|
143
147
|
ctx.actions.set_editing([i, j]);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Headers, HeadersWithIDs, TableCell, TableData } from "../types";
|
|
2
2
|
import type { SortDirection } from "./sort_utils";
|
|
3
|
+
import type { FilterDatatype } from "./filter_utils";
|
|
3
4
|
export declare function make_cell_id(row: number, col: number): string;
|
|
4
5
|
export declare function make_header_id(col: number): string;
|
|
5
6
|
export declare function get_max(data: TableData): TableCell[];
|
|
@@ -7,6 +8,12 @@ export declare function sort_table_data(data: TableData, display_value: string[]
|
|
|
7
8
|
col: number;
|
|
8
9
|
direction: SortDirection;
|
|
9
10
|
}[]): void;
|
|
11
|
+
export declare function filter_table_data(data: TableData, display_value: string[][] | null, styling: string[][] | null, filter_columns: {
|
|
12
|
+
col: number;
|
|
13
|
+
datatype: FilterDatatype;
|
|
14
|
+
filter: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}[], original_data?: TableData, original_display_value?: string[][] | null, original_styling?: string[][] | null): void;
|
|
10
17
|
export declare function copy_table_data(data: TableData, selected_cells: [number, number][] | null): Promise<void>;
|
|
11
18
|
export declare function guess_delimiter(text: string, possibleDelimiters: string[]): string[];
|
|
12
19
|
export declare function data_uri_to_blob(data_uri: string): Blob;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sort_data } from "./sort_utils";
|
|
2
|
+
import { filter_data } from "./filter_utils";
|
|
2
3
|
import { dsvFormat } from "d3-dsv";
|
|
3
4
|
export function make_cell_id(row, col) {
|
|
4
5
|
return `cell-${row}-${col}`;
|
|
@@ -36,6 +37,34 @@ export function sort_table_data(data, display_value, styling, sort_columns) {
|
|
|
36
37
|
styling.splice(0, styling.length, ...new_styling);
|
|
37
38
|
}
|
|
38
39
|
}
|
|
40
|
+
export function filter_table_data(data, display_value, styling, filter_columns, original_data, original_display_value, original_styling) {
|
|
41
|
+
const base_data = original_data ?? data;
|
|
42
|
+
const base_display_value = original_display_value ?? display_value;
|
|
43
|
+
const base_styling = original_styling ?? styling;
|
|
44
|
+
if (!filter_columns.length) {
|
|
45
|
+
data.splice(0, data.length, ...base_data.map((row) => [...row]));
|
|
46
|
+
if (display_value && base_display_value) {
|
|
47
|
+
display_value.splice(0, display_value.length, ...base_display_value.map((row) => [...row]));
|
|
48
|
+
}
|
|
49
|
+
if (styling && base_styling) {
|
|
50
|
+
styling.splice(0, styling.length, ...base_styling.map((row) => [...row]));
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!data || !data.length)
|
|
55
|
+
return;
|
|
56
|
+
const indices = filter_data(base_data, filter_columns);
|
|
57
|
+
const new_data = indices.map((i) => base_data[i]);
|
|
58
|
+
data.splice(0, data.length, ...new_data);
|
|
59
|
+
if (display_value && base_display_value) {
|
|
60
|
+
const new_display = indices.map((i) => base_display_value[i]);
|
|
61
|
+
display_value.splice(0, display_value.length, ...new_display);
|
|
62
|
+
}
|
|
63
|
+
if (styling && base_styling) {
|
|
64
|
+
const new_styling = indices.map((i) => base_styling[i]);
|
|
65
|
+
styling.splice(0, styling.length, ...new_styling);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
39
68
|
export async function copy_table_data(data, selected_cells) {
|
|
40
69
|
if (!data || !data.length)
|
|
41
70
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/dataframe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
"dompurify": "^3.0.3",
|
|
18
18
|
"katex": "^0.16.7",
|
|
19
19
|
"marked": "^12.0.0",
|
|
20
|
-
"@gradio/atoms": "^0.16.
|
|
21
|
-
"@gradio/
|
|
22
|
-
"@gradio/
|
|
20
|
+
"@gradio/atoms": "^0.16.3",
|
|
21
|
+
"@gradio/button": "^0.5.6",
|
|
22
|
+
"@gradio/checkbox": "^0.4.25",
|
|
23
|
+
"@gradio/client": "^1.15.5",
|
|
23
24
|
"@gradio/icons": "^0.12.0",
|
|
24
|
-
"@gradio/markdown-code": "^0.
|
|
25
|
-
"@gradio/statustracker": "^0.10.
|
|
26
|
-
"@gradio/
|
|
27
|
-
"@gradio/
|
|
28
|
-
"@gradio/utils": "^0.10.2"
|
|
25
|
+
"@gradio/markdown-code": "^0.5.0",
|
|
26
|
+
"@gradio/statustracker": "^0.10.14",
|
|
27
|
+
"@gradio/utils": "^0.10.2",
|
|
28
|
+
"@gradio/upload": "^0.16.10"
|
|
29
29
|
},
|
|
30
30
|
"exports": {
|
|
31
31
|
".": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"svelte": "^4.0.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@gradio/preview": "^0.
|
|
47
|
+
"@gradio/preview": "^0.14.0"
|
|
48
48
|
},
|
|
49
49
|
"repository": {
|
|
50
50
|
"type": "git",
|