@gradio/dataframe 0.17.17 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/Dataframe.stories.svelte +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/FilterMenu.svelte +235 -0
- package/dist/shared/FilterMenu.svelte.d.ts +19 -0
- package/dist/shared/Table.svelte +59 -1
- package/dist/shared/TableHeader.svelte +26 -0
- package/dist/shared/TableHeader.svelte.d.ts +7 -0
- package/dist/shared/context/dataframe_context.d.ts +19 -0
- package/dist/shared/context/dataframe_context.js +46 -6
- package/dist/shared/utils/filter_utils.d.ts +28 -0
- package/dist/shared/utils/filter_utils.js +123 -0
- package/dist/shared/utils/table_utils.d.ts +7 -0
- package/dist/shared/utils/table_utils.js +29 -0
- package/package.json +7 -7
- package/shared/CellMenu.svelte +45 -1
- package/shared/CellMenuIcons.svelte +48 -0
- package/shared/FilterMenu.svelte +248 -0
- package/shared/Table.svelte +78 -4
- package/shared/TableHeader.svelte +31 -0
- package/shared/context/dataframe_context.ts +80 -17
- package/shared/utils/filter_utils.ts +207 -0
- package/shared/utils/table_utils.ts +52 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @gradio/dataframe
|
|
2
2
|
|
|
3
|
+
## 0.18.0
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- [#11392](https://github.com/gradio-app/gradio/pull/11392) [`dc760a6`](https://github.com/gradio-app/gradio/commit/dc760a6dd7d3375f99913059803a826cdf0f7102) - Add column-specific filtering to `gr.Dataframe`. Thanks @tiago-gsantos!
|
|
8
|
+
|
|
9
|
+
### Fixes
|
|
10
|
+
|
|
11
|
+
- [#11407](https://github.com/gradio-app/gradio/pull/11407) [`f3f9590`](https://github.com/gradio-app/gradio/commit/f3f9590d539b9e6fe3937a1bdc48006ee174fb8f) - Fix Styled DataFrame Sorting. Thanks @deckar01!
|
|
12
|
+
|
|
13
|
+
### Dependency updates
|
|
14
|
+
|
|
15
|
+
- @gradio/upload@0.16.9
|
|
16
|
+
- @gradio/client@1.15.4
|
|
17
|
+
- @gradio/button@0.5.5
|
|
18
|
+
|
|
3
19
|
## 0.17.17
|
|
4
20
|
|
|
5
21
|
### Fixes
|
package/Dataframe.stories.svelte
CHANGED
|
@@ -111,12 +111,11 @@
|
|
|
111
111
|
const canvas = within(canvasElement);
|
|
112
112
|
const cells = canvas.getAllByRole("cell");
|
|
113
113
|
const initial_value = cells[0].textContent;
|
|
114
|
-
|
|
115
114
|
await userEvent.click(cells[0]);
|
|
116
115
|
await userEvent.keyboard("new value");
|
|
117
116
|
|
|
118
117
|
const final_value = cells[0].textContent;
|
|
119
|
-
if (initial_value !== final_value) {
|
|
118
|
+
if (initial_value?.trim() !== final_value?.trim()) {
|
|
120
119
|
throw new Error("Cell content changed when it should be non-editable");
|
|
121
120
|
}
|
|
122
121
|
|
package/Index.svelte
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import type { Headers, Datatype, DataframeValue } from "./shared/utils";
|
|
15
15
|
import Image from "@gradio/image";
|
|
16
16
|
|
|
17
|
-
export let headers: Headers = [];
|
|
18
17
|
export let elem_id = "";
|
|
19
18
|
export let elem_classes: string[] = [];
|
|
20
19
|
export let visible = true;
|
|
@@ -59,15 +58,6 @@
|
|
|
59
58
|
export let pinned_columns = 0;
|
|
60
59
|
export let static_columns: (string | number)[] = [];
|
|
61
60
|
export let fullscreen = false;
|
|
62
|
-
|
|
63
|
-
$: _headers = [...(value.headers || headers)];
|
|
64
|
-
$: display_value = value?.metadata?.display_value
|
|
65
|
-
? [...value?.metadata?.display_value]
|
|
66
|
-
: null;
|
|
67
|
-
$: styling =
|
|
68
|
-
!interactive && value?.metadata?.styling
|
|
69
|
-
? [...value?.metadata?.styling]
|
|
70
|
-
: null;
|
|
71
61
|
</script>
|
|
72
62
|
|
|
73
63
|
<Block
|
|
@@ -94,9 +84,9 @@
|
|
|
94
84
|
{row_count}
|
|
95
85
|
{col_count}
|
|
96
86
|
values={value.data}
|
|
97
|
-
{display_value}
|
|
98
|
-
{styling}
|
|
99
|
-
headers={
|
|
87
|
+
display_value={value.metadata?.display_value}
|
|
88
|
+
styling={value.metadata?.styling}
|
|
89
|
+
headers={value.headers}
|
|
100
90
|
{fullscreen}
|
|
101
91
|
on:change={(e) => {
|
|
102
92
|
value.data = e.detail.data;
|
package/dist/Index.svelte
CHANGED
|
@@ -8,7 +8,6 @@ export { default as BaseExample } from "./Example.svelte";
|
|
|
8
8
|
import Table from "./shared/Table.svelte";
|
|
9
9
|
import { StatusTracker } from "@gradio/statustracker";
|
|
10
10
|
import Image from "@gradio/image";
|
|
11
|
-
export let headers = [];
|
|
12
11
|
export let elem_id = "";
|
|
13
12
|
export let elem_classes = [];
|
|
14
13
|
export let visible = true;
|
|
@@ -42,12 +41,6 @@ export let show_search = "none";
|
|
|
42
41
|
export let pinned_columns = 0;
|
|
43
42
|
export let static_columns = [];
|
|
44
43
|
export let fullscreen = false;
|
|
45
|
-
$:
|
|
46
|
-
_headers = [...value.headers || headers];
|
|
47
|
-
$:
|
|
48
|
-
display_value = value?.metadata?.display_value ? [...value?.metadata?.display_value] : null;
|
|
49
|
-
$:
|
|
50
|
-
styling = !interactive && value?.metadata?.styling ? [...value?.metadata?.styling] : null;
|
|
51
44
|
</script>
|
|
52
45
|
|
|
53
46
|
<Block
|
|
@@ -74,9 +67,9 @@ $:
|
|
|
74
67
|
{row_count}
|
|
75
68
|
{col_count}
|
|
76
69
|
values={value.data}
|
|
77
|
-
{display_value}
|
|
78
|
-
{styling}
|
|
79
|
-
headers={
|
|
70
|
+
display_value={value.metadata?.display_value}
|
|
71
|
+
styling={value.metadata?.styling}
|
|
72
|
+
headers={value.headers}
|
|
80
73
|
{fullscreen}
|
|
81
74
|
on:change={(e) => {
|
|
82
75
|
value.data = e.detail.data;
|
package/dist/Index.svelte.d.ts
CHANGED
|
@@ -3,10 +3,9 @@ export { default as BaseDataFrame } from "./shared/Table.svelte";
|
|
|
3
3
|
export { default as BaseExample } from "./Example.svelte";
|
|
4
4
|
import type { Gradio, SelectData } from "@gradio/utils";
|
|
5
5
|
import type { LoadingStatus } from "@gradio/statustracker";
|
|
6
|
-
import type {
|
|
6
|
+
import type { Datatype, DataframeValue } from "./shared/utils";
|
|
7
7
|
declare const __propDef: {
|
|
8
8
|
props: {
|
|
9
|
-
headers?: Headers | undefined;
|
|
10
9
|
elem_id?: string | undefined;
|
|
11
10
|
elem_classes?: string[] | undefined;
|
|
12
11
|
visible?: boolean | undefined;
|
|
@@ -56,9 +55,6 @@ export type IndexProps = typeof __propDef.props;
|
|
|
56
55
|
export type IndexEvents = typeof __propDef.events;
|
|
57
56
|
export type IndexSlots = typeof __propDef.slots;
|
|
58
57
|
export default class Index extends SvelteComponent<IndexProps, IndexEvents, IndexSlots> {
|
|
59
|
-
get headers(): Headers | undefined;
|
|
60
|
-
/**accessor*/
|
|
61
|
-
set headers(_: Headers | undefined);
|
|
62
58
|
get elem_id(): string | undefined;
|
|
63
59
|
/**accessor*/
|
|
64
60
|
set elem_id(_: string | undefined);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>import { onMount } from "svelte";
|
|
2
2
|
import CellMenuIcons from "./CellMenuIcons.svelte";
|
|
3
|
+
import FilterMenu from "./FilterMenu.svelte";
|
|
3
4
|
export let x;
|
|
4
5
|
export let y;
|
|
5
6
|
export let on_add_row_above;
|
|
@@ -19,9 +20,15 @@ export let on_clear_sort = () => {
|
|
|
19
20
|
};
|
|
20
21
|
export let sort_direction = null;
|
|
21
22
|
export let sort_priority = null;
|
|
23
|
+
export let on_filter = () => {
|
|
24
|
+
};
|
|
25
|
+
export let on_clear_filter = () => {
|
|
26
|
+
};
|
|
27
|
+
export let filter_active = null;
|
|
22
28
|
export let editable = true;
|
|
23
29
|
export let i18n;
|
|
24
30
|
let menu_element;
|
|
31
|
+
let active_filter_menu = null;
|
|
25
32
|
$:
|
|
26
33
|
is_header = row === -1;
|
|
27
34
|
$:
|
|
@@ -48,6 +55,17 @@ function position_menu() {
|
|
|
48
55
|
menu_element.style.left = `${new_x}px`;
|
|
49
56
|
menu_element.style.top = `${new_y}px`;
|
|
50
57
|
}
|
|
58
|
+
function toggle_filter_menu() {
|
|
59
|
+
if (filter_active) {
|
|
60
|
+
on_filter("string", "", "");
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const menu_rect = menu_element.getBoundingClientRect();
|
|
64
|
+
active_filter_menu = {
|
|
65
|
+
x: menu_rect.right,
|
|
66
|
+
y: menu_rect.top + menu_rect.height / 2
|
|
67
|
+
};
|
|
68
|
+
}
|
|
51
69
|
</script>
|
|
52
70
|
|
|
53
71
|
<div bind:this={menu_element} class="cell-menu" role="menu">
|
|
@@ -78,6 +96,21 @@ function position_menu() {
|
|
|
78
96
|
<CellMenuIcons icon="clear-sort" />
|
|
79
97
|
{i18n("dataframe.clear_sort")}
|
|
80
98
|
</button>
|
|
99
|
+
<button
|
|
100
|
+
role="menuitem"
|
|
101
|
+
on:click|stopPropagation={toggle_filter_menu}
|
|
102
|
+
class:active={filter_active || active_filter_menu}
|
|
103
|
+
>
|
|
104
|
+
<CellMenuIcons icon="filter" />
|
|
105
|
+
{i18n("dataframe.filter")}
|
|
106
|
+
{#if filter_active}
|
|
107
|
+
<span class="priority">1</span>
|
|
108
|
+
{/if}
|
|
109
|
+
</button>
|
|
110
|
+
<button role="menuitem" on:click={on_clear_filter}>
|
|
111
|
+
<CellMenuIcons icon="clear-filter" />
|
|
112
|
+
{i18n("dataframe.clear_filter")}
|
|
113
|
+
</button>
|
|
81
114
|
{/if}
|
|
82
115
|
|
|
83
116
|
{#if !is_header && can_add_rows}
|
|
@@ -140,6 +173,10 @@ function position_menu() {
|
|
|
140
173
|
{/if}
|
|
141
174
|
</div>
|
|
142
175
|
|
|
176
|
+
{#if active_filter_menu}
|
|
177
|
+
<FilterMenu {on_filter} />
|
|
178
|
+
{/if}
|
|
179
|
+
|
|
143
180
|
<style>
|
|
144
181
|
.cell-menu {
|
|
145
182
|
position: fixed;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
import type { I18nFormatter } from "js/utils/src";
|
|
3
|
-
import type { SortDirection } from "./context/dataframe_context";
|
|
3
|
+
import type { SortDirection, FilterDatatype } from "./context/dataframe_context";
|
|
4
4
|
declare const __propDef: {
|
|
5
5
|
props: {
|
|
6
6
|
x: number;
|
|
@@ -20,6 +20,9 @@ declare const __propDef: {
|
|
|
20
20
|
on_clear_sort?: (() => void) | undefined;
|
|
21
21
|
sort_direction?: (SortDirection | null) | undefined;
|
|
22
22
|
sort_priority?: (number | null) | undefined;
|
|
23
|
+
on_filter?: ((datatype: FilterDatatype, selected_filter: string, value: string) => void) | undefined;
|
|
24
|
+
on_clear_filter?: (() => void) | undefined;
|
|
25
|
+
filter_active?: (boolean | null) | undefined;
|
|
23
26
|
editable?: boolean | undefined;
|
|
24
27
|
i18n: I18nFormatter;
|
|
25
28
|
};
|
|
@@ -188,4 +188,52 @@
|
|
|
188
188
|
stroke-linecap="round"
|
|
189
189
|
/>
|
|
190
190
|
</svg>
|
|
191
|
+
{:else if icon == "filter"}
|
|
192
|
+
<svg viewBox="0 0 24 24" width="16" height="16">
|
|
193
|
+
<path
|
|
194
|
+
d="M5 5H19"
|
|
195
|
+
stroke="currentColor"
|
|
196
|
+
stroke-width="2"
|
|
197
|
+
stroke-linecap="round"
|
|
198
|
+
/>
|
|
199
|
+
<path
|
|
200
|
+
d="M8 9H16"
|
|
201
|
+
stroke="currentColor"
|
|
202
|
+
stroke-width="2"
|
|
203
|
+
stroke-linecap="round"
|
|
204
|
+
/>
|
|
205
|
+
<path
|
|
206
|
+
d="M11 13H13"
|
|
207
|
+
stroke="currentColor"
|
|
208
|
+
stroke-width="2"
|
|
209
|
+
stroke-linecap="round"
|
|
210
|
+
/>
|
|
211
|
+
</svg>
|
|
212
|
+
{:else if icon == "clear-filter"}
|
|
213
|
+
<svg viewBox="0 0 24 24" width="16" height="16">
|
|
214
|
+
<path
|
|
215
|
+
d="M5 5H19"
|
|
216
|
+
stroke="currentColor"
|
|
217
|
+
stroke-width="2"
|
|
218
|
+
stroke-linecap="round"
|
|
219
|
+
/>
|
|
220
|
+
<path
|
|
221
|
+
d="M8 9H16"
|
|
222
|
+
stroke="currentColor"
|
|
223
|
+
stroke-width="2"
|
|
224
|
+
stroke-linecap="round"
|
|
225
|
+
/>
|
|
226
|
+
<path
|
|
227
|
+
d="M11 13H13"
|
|
228
|
+
stroke="currentColor"
|
|
229
|
+
stroke-width="2"
|
|
230
|
+
stroke-linecap="round"
|
|
231
|
+
/>
|
|
232
|
+
<path
|
|
233
|
+
d="M17 17L21 21M21 17L17 21"
|
|
234
|
+
stroke="currentColor"
|
|
235
|
+
stroke-width="2"
|
|
236
|
+
stroke-linecap="round"
|
|
237
|
+
/>
|
|
238
|
+
</svg>
|
|
191
239
|
{/if}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
<script>import { onMount } from "svelte";
|
|
2
|
+
import { Check } from "@gradio/icons";
|
|
3
|
+
import DropdownArrow from "../../icons/src/DropdownArrow.svelte";
|
|
4
|
+
export let on_filter = () => {
|
|
5
|
+
};
|
|
6
|
+
let menu_element;
|
|
7
|
+
let datatype = "string";
|
|
8
|
+
let current_filter = "Contains";
|
|
9
|
+
let filter_dropdown_open = false;
|
|
10
|
+
let filter_input_value = "";
|
|
11
|
+
const filter_options = {
|
|
12
|
+
string: [
|
|
13
|
+
"Contains",
|
|
14
|
+
"Does not contain",
|
|
15
|
+
"Starts with",
|
|
16
|
+
"Ends with",
|
|
17
|
+
"Is",
|
|
18
|
+
"Is not",
|
|
19
|
+
"Is empty",
|
|
20
|
+
"Is not empty"
|
|
21
|
+
],
|
|
22
|
+
number: ["=", "\u2260", ">", "<", "\u2265", "\u2264", "Is empty", "Is not empty"]
|
|
23
|
+
};
|
|
24
|
+
onMount(() => {
|
|
25
|
+
position_menu();
|
|
26
|
+
});
|
|
27
|
+
function position_menu() {
|
|
28
|
+
if (!menu_element)
|
|
29
|
+
return;
|
|
30
|
+
const viewport_width = window.innerWidth;
|
|
31
|
+
const viewport_height = window.innerHeight;
|
|
32
|
+
const menu_rect = menu_element.getBoundingClientRect();
|
|
33
|
+
const x = (viewport_width - menu_rect.width) / 2;
|
|
34
|
+
const y = (viewport_height - menu_rect.height) / 2;
|
|
35
|
+
menu_element.style.left = `${x}px`;
|
|
36
|
+
menu_element.style.top = `${y}px`;
|
|
37
|
+
}
|
|
38
|
+
function handle_filter_input(e) {
|
|
39
|
+
const target = e.target;
|
|
40
|
+
filter_input_value = target.value;
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<div>
|
|
45
|
+
<div class="background"></div>
|
|
46
|
+
<div bind:this={menu_element} class="filter-menu">
|
|
47
|
+
<div class="filter-datatype-container">
|
|
48
|
+
<span>Filter as</span>
|
|
49
|
+
<button
|
|
50
|
+
on:click|stopPropagation={() => {
|
|
51
|
+
datatype = datatype === "string" ? "number" : "string";
|
|
52
|
+
current_filter = filter_options[datatype][0];
|
|
53
|
+
}}
|
|
54
|
+
aria-label={`Change filter type. Filtering ${datatype}s`}
|
|
55
|
+
>
|
|
56
|
+
{datatype}
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="input-container">
|
|
61
|
+
<div class="filter-dropdown">
|
|
62
|
+
<button
|
|
63
|
+
on:click|stopPropagation={() =>
|
|
64
|
+
(filter_dropdown_open = !filter_dropdown_open)}
|
|
65
|
+
aria-label={`Change filter. Using '${current_filter}'`}
|
|
66
|
+
>
|
|
67
|
+
{current_filter}
|
|
68
|
+
<DropdownArrow />
|
|
69
|
+
</button>
|
|
70
|
+
|
|
71
|
+
{#if filter_dropdown_open}
|
|
72
|
+
<div class="dropdown-filter-options">
|
|
73
|
+
{#each filter_options[datatype] as opt}
|
|
74
|
+
<button
|
|
75
|
+
on:click|stopPropagation={() => {
|
|
76
|
+
current_filter = opt;
|
|
77
|
+
filter_dropdown_open = !filter_dropdown_open;
|
|
78
|
+
}}
|
|
79
|
+
class="filter-option"
|
|
80
|
+
>
|
|
81
|
+
{opt}
|
|
82
|
+
</button>
|
|
83
|
+
{/each}
|
|
84
|
+
</div>
|
|
85
|
+
{/if}
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<input
|
|
89
|
+
type="text"
|
|
90
|
+
value={filter_input_value}
|
|
91
|
+
on:click|stopPropagation
|
|
92
|
+
on:input={handle_filter_input}
|
|
93
|
+
placeholder="Type a value"
|
|
94
|
+
class="filter-input"
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<button
|
|
99
|
+
class="check-button"
|
|
100
|
+
on:click={() => on_filter(datatype, current_filter, filter_input_value)}
|
|
101
|
+
>
|
|
102
|
+
<Check />
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<style>
|
|
108
|
+
.background {
|
|
109
|
+
position: fixed;
|
|
110
|
+
top: 0;
|
|
111
|
+
left: 0;
|
|
112
|
+
width: 100vw;
|
|
113
|
+
height: 100vh;
|
|
114
|
+
background-color: rgba(0, 0, 0, 0.4);
|
|
115
|
+
z-index: 20;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.filter-menu {
|
|
119
|
+
position: fixed;
|
|
120
|
+
background: var(--background-fill-primary);
|
|
121
|
+
border: 1px solid var(--border-color-primary);
|
|
122
|
+
border-radius: var(--radius-sm);
|
|
123
|
+
padding: var(--size-2);
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
gap: var(--size-2);
|
|
127
|
+
box-shadow: var(--shadow-drop-lg);
|
|
128
|
+
width: 300px;
|
|
129
|
+
z-index: 21;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.filter-datatype-container {
|
|
133
|
+
display: flex;
|
|
134
|
+
gap: var(--size-2);
|
|
135
|
+
align-items: center;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.filter-menu span {
|
|
139
|
+
font-size: var(--text-sm);
|
|
140
|
+
color: var(--body-text-color);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.filter-menu button {
|
|
144
|
+
height: var(--size-6);
|
|
145
|
+
background: none;
|
|
146
|
+
border: 1px solid var(--border-color-primary);
|
|
147
|
+
border-radius: var(--radius-sm);
|
|
148
|
+
padding: var(--size-1) var(--size-2);
|
|
149
|
+
color: var(--body-text-color);
|
|
150
|
+
font-size: var(--text-sm);
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
gap: var(--size-2);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.filter-menu button:hover {
|
|
159
|
+
background-color: var(--background-fill-secondary);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.filter-input {
|
|
163
|
+
width: var(--size-full);
|
|
164
|
+
height: var(--size-6);
|
|
165
|
+
padding: var(--size-2);
|
|
166
|
+
padding-right: var(--size-8);
|
|
167
|
+
border: 1px solid var(--border-color-primary);
|
|
168
|
+
border-radius: var(--table-radius);
|
|
169
|
+
font-size: var(--text-sm);
|
|
170
|
+
color: var(--body-text-color);
|
|
171
|
+
background: var(--background-fill-secondary);
|
|
172
|
+
transition: all 0.2s ease;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.filter-input:hover {
|
|
176
|
+
border-color: var(--border-color-secondary);
|
|
177
|
+
background: var(--background-fill-primary);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.filter-input:focus {
|
|
181
|
+
outline: none;
|
|
182
|
+
border-color: var(--color-accent);
|
|
183
|
+
background: var(--background-fill-primary);
|
|
184
|
+
box-shadow: 0 0 0 1px var(--color-accent);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.dropdown-filter-options {
|
|
188
|
+
display: flex;
|
|
189
|
+
flex-direction: column;
|
|
190
|
+
background: var(--background-fill-primary);
|
|
191
|
+
border: 1px solid var(--border-color-primary);
|
|
192
|
+
border-radius: var(--radius-sm);
|
|
193
|
+
box-shadow: var(--shadow-drop-md);
|
|
194
|
+
position: absolute;
|
|
195
|
+
z-index: var(--layer-1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.dropdown-filter-options .filter-option {
|
|
199
|
+
border: none;
|
|
200
|
+
justify-content: flex-start;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.input-container {
|
|
204
|
+
display: flex;
|
|
205
|
+
gap: var(--size-2);
|
|
206
|
+
align-items: center;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.input-container button {
|
|
210
|
+
width: 130px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
:global(svg.dropdown-arrow) {
|
|
214
|
+
width: var(--size-4);
|
|
215
|
+
height: var(--size-4);
|
|
216
|
+
margin-left: auto;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.filter-menu .check-button {
|
|
220
|
+
background: var(--color-accent);
|
|
221
|
+
color: white;
|
|
222
|
+
border: none;
|
|
223
|
+
width: var(--size-full);
|
|
224
|
+
height: var(--size-6);
|
|
225
|
+
border-radius: var(--radius-sm);
|
|
226
|
+
display: flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
padding: var(--size-1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.check-button:hover {
|
|
233
|
+
background: var(--color-accent-soft);
|
|
234
|
+
}
|
|
235
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { FilterDatatype } from "./context/dataframe_context";
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
on_filter?: ((datatype: FilterDatatype, selected_filter: string, value: string) => void) | undefined;
|
|
6
|
+
};
|
|
7
|
+
events: {
|
|
8
|
+
click: MouseEvent;
|
|
9
|
+
} & {
|
|
10
|
+
[evt: string]: CustomEvent<any>;
|
|
11
|
+
};
|
|
12
|
+
slots: {};
|
|
13
|
+
};
|
|
14
|
+
export type FilterMenuProps = typeof __propDef.props;
|
|
15
|
+
export type FilterMenuEvents = typeof __propDef.events;
|
|
16
|
+
export type FilterMenuSlots = typeof __propDef.slots;
|
|
17
|
+
export default class FilterMenu extends SvelteComponent<FilterMenuProps, FilterMenuEvents, FilterMenuSlots> {
|
|
18
|
+
}
|
|
19
|
+
export {};
|
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
|
|