@gradio/dataframe 0.16.4 → 0.17.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 +36 -0
- package/Dataframe.stories.svelte +202 -9
- package/Index.svelte +7 -13
- package/dist/Index.svelte +5 -9
- package/dist/Index.svelte.d.ts +9 -2
- package/dist/shared/CellMenu.svelte +53 -1
- package/dist/shared/CellMenu.svelte.d.ts +5 -0
- package/dist/shared/CellMenuButton.svelte +44 -0
- package/dist/shared/CellMenuButton.svelte.d.ts +16 -0
- package/dist/shared/CellMenuIcons.svelte +79 -0
- package/dist/shared/EditableCell.svelte +83 -14
- package/dist/shared/EditableCell.svelte.d.ts +12 -3
- package/dist/shared/EmptyRowButton.svelte +28 -0
- package/dist/shared/EmptyRowButton.svelte.d.ts +16 -0
- package/dist/shared/RowNumber.svelte +40 -0
- package/dist/shared/RowNumber.svelte.d.ts +17 -0
- package/dist/shared/Table.svelte +543 -1110
- package/dist/shared/Table.svelte.d.ts +4 -0
- package/dist/shared/TableCell.svelte +291 -0
- package/dist/shared/TableCell.svelte.d.ts +57 -0
- package/dist/shared/TableHeader.svelte +239 -0
- package/dist/shared/TableHeader.svelte.d.ts +45 -0
- package/dist/shared/Toolbar.svelte +18 -8
- package/dist/shared/VirtualTable.svelte +66 -19
- package/dist/shared/VirtualTable.svelte.d.ts +4 -0
- package/dist/shared/context/keyboard_context.d.ts +37 -0
- package/dist/shared/context/keyboard_context.js +12 -0
- package/dist/shared/context/selection_context.d.ts +32 -0
- package/dist/shared/context/selection_context.js +107 -0
- package/dist/shared/context/table_context.d.ts +141 -0
- package/dist/shared/context/table_context.js +375 -0
- package/dist/shared/icons/Padlock.svelte +24 -0
- package/dist/shared/icons/Padlock.svelte.d.ts +23 -0
- package/dist/shared/icons/SelectionButtons.svelte +85 -0
- package/dist/shared/icons/SelectionButtons.svelte.d.ts +18 -0
- package/dist/shared/icons/SortArrowDown.svelte +24 -0
- package/dist/shared/icons/SortArrowDown.svelte.d.ts +16 -0
- package/dist/shared/icons/SortArrowUp.svelte +24 -0
- package/dist/shared/icons/SortArrowUp.svelte.d.ts +16 -0
- package/dist/shared/icons/SortButtonDown.svelte +14 -0
- package/dist/shared/icons/SortButtonDown.svelte.d.ts +23 -0
- package/dist/shared/icons/SortButtonUp.svelte +15 -0
- package/dist/shared/icons/SortButtonUp.svelte.d.ts +23 -0
- package/dist/shared/icons/SortIcon.svelte +46 -68
- package/dist/shared/icons/SortIcon.svelte.d.ts +3 -2
- package/dist/shared/selection_utils.d.ts +2 -1
- package/dist/shared/selection_utils.js +39 -10
- package/dist/shared/utils/data_processing.d.ts +13 -0
- package/dist/shared/utils/data_processing.js +45 -0
- package/dist/shared/utils/drag_utils.d.ts +15 -0
- package/dist/shared/utils/drag_utils.js +57 -0
- package/dist/shared/utils/keyboard_utils.d.ts +2 -0
- package/dist/shared/utils/keyboard_utils.js +186 -0
- package/dist/shared/utils/sort_utils.d.ts +9 -3
- package/dist/shared/utils/sort_utils.js +30 -24
- package/dist/shared/utils/table_utils.d.ts +6 -5
- package/dist/shared/utils/table_utils.js +13 -56
- package/package.json +9 -9
- package/shared/CellMenu.svelte +52 -1
- package/shared/CellMenuButton.svelte +45 -0
- package/shared/CellMenuIcons.svelte +79 -0
- package/shared/EditableCell.svelte +97 -18
- package/shared/EmptyRowButton.svelte +29 -0
- package/shared/RowNumber.svelte +41 -0
- package/shared/Table.svelte +568 -1223
- package/shared/TableCell.svelte +324 -0
- package/shared/TableHeader.svelte +256 -0
- package/shared/Toolbar.svelte +19 -8
- package/shared/VirtualTable.svelte +72 -19
- package/shared/context/keyboard_context.ts +65 -0
- package/shared/context/selection_context.ts +168 -0
- package/shared/context/table_context.ts +625 -0
- package/shared/icons/Padlock.svelte +24 -0
- package/shared/icons/SelectionButtons.svelte +93 -0
- package/shared/icons/SortArrowDown.svelte +25 -0
- package/shared/icons/SortArrowUp.svelte +25 -0
- package/shared/icons/SortButtonDown.svelte +14 -0
- package/shared/icons/SortButtonUp.svelte +15 -0
- package/shared/icons/SortIcon.svelte +47 -70
- package/shared/selection_utils.ts +39 -13
- package/shared/utils/data_processing.ts +72 -0
- package/shared/utils/drag_utils.ts +92 -0
- package/shared/utils/keyboard_utils.ts +238 -0
- package/shared/utils/sort_utils.test.ts +262 -14
- package/shared/utils/sort_utils.ts +39 -31
- package/shared/utils/table_utils.test.ts +66 -45
- package/shared/utils/table_utils.ts +16 -86
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import type { Headers, HeadersWithIDs, TableCell, TableData
|
|
1
|
+
import type { Headers, HeadersWithIDs, TableCell, TableData } from "../types";
|
|
2
2
|
import type { SortDirection } from "./sort_utils";
|
|
3
3
|
export declare function make_cell_id(row: number, col: number): string;
|
|
4
4
|
export declare function make_header_id(col: number): string;
|
|
5
|
-
export declare function process_data(input_values: (string | number)[][], row_count: CountConfig, col_count: CountConfig, headers: Headers, show_row_numbers: boolean, element_refs: ElementRefs, data_binding: DataBinding): TableData;
|
|
6
|
-
export declare function make_headers(input_headers: Headers, show_row_numbers: boolean, col_count: CountConfig, element_refs: ElementRefs): HeadersWithIDs[];
|
|
7
5
|
export declare function get_max(data: TableData): TableCell[];
|
|
8
|
-
export declare function sort_table_data(data: TableData, display_value: string[][] | null, styling: string[][] | null,
|
|
9
|
-
|
|
6
|
+
export declare function sort_table_data(data: TableData, display_value: string[][] | null, styling: string[][] | null, sort_columns: {
|
|
7
|
+
col: number;
|
|
8
|
+
direction: SortDirection;
|
|
9
|
+
}[]): void;
|
|
10
|
+
export declare function copy_table_data(data: TableData, selected_cells: [number, number][] | null): Promise<void>;
|
|
10
11
|
export declare function guess_delimiter(text: string, possibleDelimiters: string[]): string[];
|
|
11
12
|
export declare function data_uri_to_blob(data_uri: string): Blob;
|
|
12
13
|
export declare function handle_file_upload(data_uri: string, update_headers: (headers: Headers) => HeadersWithIDs[], update_values: (values: (string | number)[][]) => void): void;
|
|
@@ -6,58 +6,6 @@ export function make_cell_id(row, col) {
|
|
|
6
6
|
export function make_header_id(col) {
|
|
7
7
|
return `header-${col}`;
|
|
8
8
|
}
|
|
9
|
-
export function process_data(input_values, row_count, col_count, headers, show_row_numbers, element_refs, data_binding) {
|
|
10
|
-
const data_row_length = input_values.length;
|
|
11
|
-
return Array(row_count[1] === "fixed" ? row_count[0] : data_row_length)
|
|
12
|
-
.fill(0)
|
|
13
|
-
.map((_, row) => {
|
|
14
|
-
return Array(col_count[1] === "fixed"
|
|
15
|
-
? col_count[0]
|
|
16
|
-
: data_row_length > 0
|
|
17
|
-
? input_values[0].length
|
|
18
|
-
: headers.length)
|
|
19
|
-
.fill(0)
|
|
20
|
-
.map((_, col) => {
|
|
21
|
-
const cell_id = make_cell_id(row, col);
|
|
22
|
-
element_refs[cell_id] = element_refs[cell_id] || {
|
|
23
|
-
input: null,
|
|
24
|
-
cell: null
|
|
25
|
-
};
|
|
26
|
-
const cell_obj = {
|
|
27
|
-
value: input_values?.[row]?.[col] ?? "",
|
|
28
|
-
id: cell_id
|
|
29
|
-
};
|
|
30
|
-
data_binding[cell_id] = cell_obj;
|
|
31
|
-
return cell_obj;
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
export function make_headers(input_headers, show_row_numbers, col_count, element_refs) {
|
|
36
|
-
let header_list = input_headers || [];
|
|
37
|
-
if (show_row_numbers) {
|
|
38
|
-
header_list = ["", ...header_list];
|
|
39
|
-
}
|
|
40
|
-
if (col_count[1] === "fixed" && header_list.length < col_count[0]) {
|
|
41
|
-
const fill_headers = Array(col_count[0] - header_list.length)
|
|
42
|
-
.fill("")
|
|
43
|
-
.map((_, i) => `${i + header_list.length}`);
|
|
44
|
-
header_list = header_list.concat(fill_headers);
|
|
45
|
-
}
|
|
46
|
-
if (!header_list || header_list.length === 0) {
|
|
47
|
-
return Array(col_count[0])
|
|
48
|
-
.fill(0)
|
|
49
|
-
.map((_, col) => {
|
|
50
|
-
const header_id = make_header_id(col);
|
|
51
|
-
element_refs[header_id] = { cell: null, input: null };
|
|
52
|
-
return { id: header_id, value: JSON.stringify(col + 1) };
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return header_list.map((header, col) => {
|
|
56
|
-
const header_id = make_header_id(col);
|
|
57
|
-
element_refs[header_id] = { cell: null, input: null };
|
|
58
|
-
return { id: header_id, value: header ?? "" };
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
9
|
export function get_max(data) {
|
|
62
10
|
if (!data || !data.length)
|
|
63
11
|
return [];
|
|
@@ -71,8 +19,12 @@ export function get_max(data) {
|
|
|
71
19
|
}
|
|
72
20
|
return max;
|
|
73
21
|
}
|
|
74
|
-
export function sort_table_data(data, display_value, styling,
|
|
75
|
-
|
|
22
|
+
export function sort_table_data(data, display_value, styling, sort_columns) {
|
|
23
|
+
if (!sort_columns.length)
|
|
24
|
+
return;
|
|
25
|
+
if (!data || !data.length)
|
|
26
|
+
return;
|
|
27
|
+
const indices = sort_data(data, sort_columns);
|
|
76
28
|
const new_data = indices.map((i) => data[i]);
|
|
77
29
|
data.splice(0, data.length, ...new_data);
|
|
78
30
|
if (display_value) {
|
|
@@ -85,7 +37,11 @@ export function sort_table_data(data, display_value, styling, col, dir) {
|
|
|
85
37
|
}
|
|
86
38
|
}
|
|
87
39
|
export async function copy_table_data(data, selected_cells) {
|
|
88
|
-
|
|
40
|
+
if (!data || !data.length)
|
|
41
|
+
return;
|
|
42
|
+
const cells_to_copy = selected_cells ||
|
|
43
|
+
data.flatMap((row, r) => row.map((_, c) => [r, c]));
|
|
44
|
+
const csv = cells_to_copy.reduce((acc, [row, col]) => {
|
|
89
45
|
acc[row] = acc[row] || {};
|
|
90
46
|
const value = String(data[row][col].value);
|
|
91
47
|
acc[row][col] =
|
|
@@ -95,6 +51,8 @@ export async function copy_table_data(data, selected_cells) {
|
|
|
95
51
|
return acc;
|
|
96
52
|
}, {});
|
|
97
53
|
const rows = Object.keys(csv).sort((a, b) => +a - +b);
|
|
54
|
+
if (!rows.length)
|
|
55
|
+
return;
|
|
98
56
|
const cols = Object.keys(csv[rows[0]]).sort((a, b) => +a - +b);
|
|
99
57
|
const text = rows
|
|
100
58
|
.map((r) => cols.map((c) => csv[r][c] || "").join(","))
|
|
@@ -103,7 +61,6 @@ export async function copy_table_data(data, selected_cells) {
|
|
|
103
61
|
await navigator.clipboard.writeText(text);
|
|
104
62
|
}
|
|
105
63
|
catch (err) {
|
|
106
|
-
console.error("Copy failed:", err);
|
|
107
64
|
throw new Error("Failed to copy to clipboard: " + err.message);
|
|
108
65
|
}
|
|
109
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gradio/dataframe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Gradio UI packages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "",
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
"dompurify": "^3.0.3",
|
|
18
18
|
"katex": "^0.16.7",
|
|
19
19
|
"marked": "^12.0.0",
|
|
20
|
-
"@gradio/atoms": "^0.
|
|
21
|
-
"@gradio/button": "^0.4.
|
|
22
|
-
"@gradio/
|
|
23
|
-
"@gradio/
|
|
24
|
-
"@gradio/statustracker": "^0.10.
|
|
25
|
-
"@gradio/upload": "^0.15.
|
|
26
|
-
"@gradio/
|
|
27
|
-
"@gradio/
|
|
20
|
+
"@gradio/atoms": "^0.14.0",
|
|
21
|
+
"@gradio/button": "^0.4.10",
|
|
22
|
+
"@gradio/markdown-code": "^0.4.1",
|
|
23
|
+
"@gradio/client": "^1.13.1",
|
|
24
|
+
"@gradio/statustracker": "^0.10.5",
|
|
25
|
+
"@gradio/upload": "^0.15.5",
|
|
26
|
+
"@gradio/utils": "^0.10.1",
|
|
27
|
+
"@gradio/icons": "^0.10.0"
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
package/shared/CellMenu.svelte
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
3
|
import CellMenuIcons from "./CellMenuIcons.svelte";
|
|
4
4
|
import type { I18nFormatter } from "js/utils/src";
|
|
5
|
+
import type { SortDirection } from "./context/table_context";
|
|
5
6
|
|
|
6
7
|
export let x: number;
|
|
7
8
|
export let y: number;
|
|
@@ -16,6 +17,10 @@
|
|
|
16
17
|
export let on_delete_col: () => void;
|
|
17
18
|
export let can_delete_rows: boolean;
|
|
18
19
|
export let can_delete_cols: boolean;
|
|
20
|
+
export let on_sort: (direction: SortDirection) => void = () => {};
|
|
21
|
+
export let on_clear_sort: () => void = () => {};
|
|
22
|
+
export let sort_direction: SortDirection | null = null;
|
|
23
|
+
export let sort_priority: number | null = null;
|
|
19
24
|
|
|
20
25
|
export let i18n: I18nFormatter;
|
|
21
26
|
let menu_element: HTMLDivElement;
|
|
@@ -52,6 +57,33 @@
|
|
|
52
57
|
</script>
|
|
53
58
|
|
|
54
59
|
<div bind:this={menu_element} class="cell-menu">
|
|
60
|
+
{#if is_header}
|
|
61
|
+
<button
|
|
62
|
+
on:click={() => on_sort("asc")}
|
|
63
|
+
class:active={sort_direction === "asc"}
|
|
64
|
+
>
|
|
65
|
+
<CellMenuIcons icon="sort-asc" />
|
|
66
|
+
{i18n("dataframe.sort_ascending")}
|
|
67
|
+
{#if sort_direction === "asc" && sort_priority !== null}
|
|
68
|
+
<span class="priority">{sort_priority}</span>
|
|
69
|
+
{/if}
|
|
70
|
+
</button>
|
|
71
|
+
<button
|
|
72
|
+
on:click={() => on_sort("desc")}
|
|
73
|
+
class:active={sort_direction === "desc"}
|
|
74
|
+
>
|
|
75
|
+
<CellMenuIcons icon="sort-desc" />
|
|
76
|
+
{i18n("dataframe.sort_descending")}
|
|
77
|
+
{#if sort_direction === "desc" && sort_priority !== null}
|
|
78
|
+
<span class="priority">{sort_priority}</span>
|
|
79
|
+
{/if}
|
|
80
|
+
</button>
|
|
81
|
+
<button on:click={on_clear_sort}>
|
|
82
|
+
<CellMenuIcons icon="clear-sort" />
|
|
83
|
+
{i18n("dataframe.clear_sort")}
|
|
84
|
+
</button>
|
|
85
|
+
{/if}
|
|
86
|
+
|
|
55
87
|
{#if !is_header && can_add_rows}
|
|
56
88
|
<button on:click={() => on_add_row_above()}>
|
|
57
89
|
<CellMenuIcons icon="add-row-above" />
|
|
@@ -89,7 +121,7 @@
|
|
|
89
121
|
<style>
|
|
90
122
|
.cell-menu {
|
|
91
123
|
position: fixed;
|
|
92
|
-
z-index:
|
|
124
|
+
z-index: 9;
|
|
93
125
|
background: var(--background-fill-primary);
|
|
94
126
|
border: 1px solid var(--border-color-primary);
|
|
95
127
|
border-radius: var(--radius-sm);
|
|
@@ -99,6 +131,7 @@
|
|
|
99
131
|
gap: var(--size-1);
|
|
100
132
|
box-shadow: var(--shadow-drop-lg);
|
|
101
133
|
min-width: 150px;
|
|
134
|
+
z-index: var(--layer-1);
|
|
102
135
|
}
|
|
103
136
|
|
|
104
137
|
.cell-menu button {
|
|
@@ -116,6 +149,11 @@
|
|
|
116
149
|
display: flex;
|
|
117
150
|
align-items: center;
|
|
118
151
|
gap: var(--size-2);
|
|
152
|
+
position: relative;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.cell-menu button.active {
|
|
156
|
+
background-color: var(--background-fill-secondary);
|
|
119
157
|
}
|
|
120
158
|
|
|
121
159
|
.cell-menu button:hover {
|
|
@@ -126,4 +164,17 @@
|
|
|
126
164
|
fill: currentColor;
|
|
127
165
|
transition: fill 0.2s;
|
|
128
166
|
}
|
|
167
|
+
|
|
168
|
+
.priority {
|
|
169
|
+
display: flex;
|
|
170
|
+
align-items: center;
|
|
171
|
+
justify-content: center;
|
|
172
|
+
margin-left: auto;
|
|
173
|
+
font-size: var(--size-2);
|
|
174
|
+
background-color: var(--button-secondary-background-fill);
|
|
175
|
+
color: var(--body-text-color);
|
|
176
|
+
border-radius: var(--radius-sm);
|
|
177
|
+
width: var(--size-2-5);
|
|
178
|
+
height: var(--size-2-5);
|
|
179
|
+
}
|
|
129
180
|
</style>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let on_click: (event: MouseEvent) => void;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<button
|
|
6
|
+
aria-label="Open cell menu"
|
|
7
|
+
class="cell-menu-button"
|
|
8
|
+
on:click={on_click}
|
|
9
|
+
on:touchstart={(event) => {
|
|
10
|
+
event.preventDefault();
|
|
11
|
+
const touch = event.touches[0];
|
|
12
|
+
const mouseEvent = new MouseEvent("click", {
|
|
13
|
+
clientX: touch.clientX,
|
|
14
|
+
clientY: touch.clientY,
|
|
15
|
+
bubbles: true,
|
|
16
|
+
cancelable: true,
|
|
17
|
+
view: window
|
|
18
|
+
});
|
|
19
|
+
on_click(mouseEvent);
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
⋮
|
|
23
|
+
</button>
|
|
24
|
+
|
|
25
|
+
<style>
|
|
26
|
+
.cell-menu-button {
|
|
27
|
+
flex-shrink: 0;
|
|
28
|
+
display: none;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
background-color: var(--block-background-fill);
|
|
32
|
+
border: 1px solid var(--border-color-primary);
|
|
33
|
+
border-radius: var(--block-radius);
|
|
34
|
+
width: var(--size-5);
|
|
35
|
+
height: var(--size-5);
|
|
36
|
+
min-width: var(--size-5);
|
|
37
|
+
padding: 0;
|
|
38
|
+
margin-right: var(--spacing-sm);
|
|
39
|
+
z-index: 2;
|
|
40
|
+
position: absolute;
|
|
41
|
+
right: var(--size-1);
|
|
42
|
+
top: 50%;
|
|
43
|
+
transform: translateY(-50%);
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
@@ -110,4 +110,83 @@
|
|
|
110
110
|
stroke-linecap="round"
|
|
111
111
|
/>
|
|
112
112
|
</svg>
|
|
113
|
+
{:else if icon == "sort-asc"}
|
|
114
|
+
<svg viewBox="0 0 24 24" width="16" height="16">
|
|
115
|
+
<path
|
|
116
|
+
d="M8 16L12 12L16 16"
|
|
117
|
+
stroke="currentColor"
|
|
118
|
+
stroke-width="2"
|
|
119
|
+
fill="none"
|
|
120
|
+
stroke-linecap="round"
|
|
121
|
+
stroke-linejoin="round"
|
|
122
|
+
/>
|
|
123
|
+
<path
|
|
124
|
+
d="M12 12V19"
|
|
125
|
+
stroke="currentColor"
|
|
126
|
+
stroke-width="2"
|
|
127
|
+
stroke-linecap="round"
|
|
128
|
+
/>
|
|
129
|
+
<path
|
|
130
|
+
d="M5 7H19"
|
|
131
|
+
stroke="currentColor"
|
|
132
|
+
stroke-width="2"
|
|
133
|
+
stroke-linecap="round"
|
|
134
|
+
/>
|
|
135
|
+
</svg>
|
|
136
|
+
{:else if icon == "sort-desc"}
|
|
137
|
+
<svg viewBox="0 0 24 24" width="16" height="16">
|
|
138
|
+
<path
|
|
139
|
+
d="M8 12L12 16L16 12"
|
|
140
|
+
stroke="currentColor"
|
|
141
|
+
stroke-width="2"
|
|
142
|
+
fill="none"
|
|
143
|
+
stroke-linecap="round"
|
|
144
|
+
stroke-linejoin="round"
|
|
145
|
+
/>
|
|
146
|
+
<path
|
|
147
|
+
d="M12 16V9"
|
|
148
|
+
stroke="currentColor"
|
|
149
|
+
stroke-width="2"
|
|
150
|
+
stroke-linecap="round"
|
|
151
|
+
/>
|
|
152
|
+
<path
|
|
153
|
+
d="M5 5H19"
|
|
154
|
+
stroke="currentColor"
|
|
155
|
+
stroke-width="2"
|
|
156
|
+
stroke-linecap="round"
|
|
157
|
+
/>
|
|
158
|
+
</svg>
|
|
159
|
+
{:else if icon == "clear-sort"}
|
|
160
|
+
<svg viewBox="0 0 24 24" width="16" height="16">
|
|
161
|
+
<path
|
|
162
|
+
d="M5 5H19"
|
|
163
|
+
stroke="currentColor"
|
|
164
|
+
stroke-width="2"
|
|
165
|
+
stroke-linecap="round"
|
|
166
|
+
/>
|
|
167
|
+
<path
|
|
168
|
+
d="M5 9H15"
|
|
169
|
+
stroke="currentColor"
|
|
170
|
+
stroke-width="2"
|
|
171
|
+
stroke-linecap="round"
|
|
172
|
+
/>
|
|
173
|
+
<path
|
|
174
|
+
d="M5 13H11"
|
|
175
|
+
stroke="currentColor"
|
|
176
|
+
stroke-width="2"
|
|
177
|
+
stroke-linecap="round"
|
|
178
|
+
/>
|
|
179
|
+
<path
|
|
180
|
+
d="M5 17H7"
|
|
181
|
+
stroke="currentColor"
|
|
182
|
+
stroke-width="2"
|
|
183
|
+
stroke-linecap="round"
|
|
184
|
+
/>
|
|
185
|
+
<path
|
|
186
|
+
d="M17 17L21 21M21 17L17 21"
|
|
187
|
+
stroke="currentColor"
|
|
188
|
+
stroke-width="2"
|
|
189
|
+
stroke-linecap="round"
|
|
190
|
+
/>
|
|
191
|
+
</svg>
|
|
113
192
|
{/if}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { createEventDispatcher } from "svelte";
|
|
3
3
|
import { MarkdownCode } from "@gradio/markdown-code";
|
|
4
|
-
|
|
4
|
+
import type { I18nFormatter } from "@gradio/utils";
|
|
5
|
+
import SelectionButtons from "./icons/SelectionButtons.svelte";
|
|
5
6
|
export let edit: boolean;
|
|
6
7
|
export let value: string | number = "";
|
|
7
8
|
export let display_value: string | null = null;
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
| "html"
|
|
14
15
|
| "number"
|
|
15
16
|
| "bool"
|
|
16
|
-
| "date"
|
|
17
|
+
| "date"
|
|
18
|
+
| "image" = "str";
|
|
17
19
|
export let latex_delimiters: {
|
|
18
20
|
left: string;
|
|
19
21
|
right: string;
|
|
@@ -22,10 +24,23 @@
|
|
|
22
24
|
export let clear_on_focus = false;
|
|
23
25
|
export let line_breaks = true;
|
|
24
26
|
export let editable = true;
|
|
27
|
+
export let is_static = false;
|
|
25
28
|
export let root: string;
|
|
26
29
|
export let max_chars: number | null = null;
|
|
30
|
+
export let components: Record<string, any> = {};
|
|
31
|
+
export let i18n: I18nFormatter;
|
|
32
|
+
export let is_dragging = false;
|
|
33
|
+
|
|
34
|
+
export let show_selection_buttons = false;
|
|
35
|
+
export let coords: [number, number] | null = null;
|
|
36
|
+
export let on_select_column: ((col: number) => void) | null = null;
|
|
37
|
+
export let on_select_row: ((row: number) => void) | null = null;
|
|
38
|
+
|
|
39
|
+
const dispatch = createEventDispatcher<{
|
|
40
|
+
blur: void;
|
|
41
|
+
keydown: KeyboardEvent;
|
|
42
|
+
}>();
|
|
27
43
|
|
|
28
|
-
const dispatch = createEventDispatcher();
|
|
29
44
|
let is_expanded = false;
|
|
30
45
|
|
|
31
46
|
export let el: HTMLInputElement | null;
|
|
@@ -33,16 +48,28 @@
|
|
|
33
48
|
|
|
34
49
|
function truncate_text(
|
|
35
50
|
text: string | number,
|
|
36
|
-
max_length: number | null = null
|
|
51
|
+
max_length: number | null = null,
|
|
52
|
+
is_image = false
|
|
37
53
|
): string {
|
|
54
|
+
if (is_image) return String(text);
|
|
38
55
|
const str = String(text);
|
|
39
|
-
if (!max_length ||
|
|
56
|
+
if (!max_length || max_length <= 0) return str;
|
|
57
|
+
if (str.length <= max_length) return str;
|
|
40
58
|
return str.slice(0, max_length) + "...";
|
|
41
59
|
}
|
|
42
60
|
|
|
43
|
-
$:
|
|
61
|
+
$: should_truncate =
|
|
62
|
+
!edit && !is_expanded && max_chars !== null && max_chars > 0;
|
|
63
|
+
|
|
64
|
+
$: display_content = editable
|
|
44
65
|
? value
|
|
45
|
-
:
|
|
66
|
+
: display_value !== null
|
|
67
|
+
? display_value
|
|
68
|
+
: value;
|
|
69
|
+
|
|
70
|
+
$: display_text = should_truncate
|
|
71
|
+
? truncate_text(display_content, max_chars, datatype === "image")
|
|
72
|
+
: display_content;
|
|
46
73
|
|
|
47
74
|
function use_focus(node: HTMLInputElement): any {
|
|
48
75
|
if (clear_on_focus) {
|
|
@@ -86,7 +113,10 @@
|
|
|
86
113
|
|
|
87
114
|
{#if edit}
|
|
88
115
|
<input
|
|
116
|
+
readonly={is_static}
|
|
117
|
+
aria-readonly={is_static}
|
|
89
118
|
role="textbox"
|
|
119
|
+
aria-label={is_static ? "Cell is read-only" : "Edit cell"}
|
|
90
120
|
bind:this={el}
|
|
91
121
|
bind:value={_value}
|
|
92
122
|
class:header
|
|
@@ -101,6 +131,7 @@
|
|
|
101
131
|
{/if}
|
|
102
132
|
|
|
103
133
|
<span
|
|
134
|
+
class:dragging={is_dragging}
|
|
104
135
|
on:click={handle_click}
|
|
105
136
|
on:keydown={handle_keydown}
|
|
106
137
|
tabindex="0"
|
|
@@ -111,9 +142,22 @@
|
|
|
111
142
|
on:focus|preventDefault
|
|
112
143
|
style={styling}
|
|
113
144
|
data-editable={editable}
|
|
145
|
+
data-max-chars={max_chars}
|
|
146
|
+
data-expanded={is_expanded}
|
|
114
147
|
placeholder=" "
|
|
148
|
+
class:text={datatype === "str"}
|
|
115
149
|
>
|
|
116
|
-
{#if datatype === "
|
|
150
|
+
{#if datatype === "image" && components.image}
|
|
151
|
+
<svelte:component
|
|
152
|
+
this={components.image}
|
|
153
|
+
value={{ url: display_text }}
|
|
154
|
+
show_label={false}
|
|
155
|
+
label="cell-image"
|
|
156
|
+
show_download_button={false}
|
|
157
|
+
{i18n}
|
|
158
|
+
gradio={{ dispatch: () => {} }}
|
|
159
|
+
/>
|
|
160
|
+
{:else if datatype === "html"}
|
|
117
161
|
{@html display_text}
|
|
118
162
|
{:else if datatype === "markdown"}
|
|
119
163
|
<MarkdownCode
|
|
@@ -124,23 +168,36 @@
|
|
|
124
168
|
{root}
|
|
125
169
|
/>
|
|
126
170
|
{:else}
|
|
127
|
-
{
|
|
171
|
+
{display_text}
|
|
128
172
|
{/if}
|
|
129
173
|
</span>
|
|
174
|
+
{#if show_selection_buttons && coords && on_select_column && on_select_row}
|
|
175
|
+
<SelectionButtons
|
|
176
|
+
position="column"
|
|
177
|
+
{coords}
|
|
178
|
+
on_click={() => on_select_column(coords[1])}
|
|
179
|
+
/>
|
|
180
|
+
<SelectionButtons
|
|
181
|
+
position="row"
|
|
182
|
+
{coords}
|
|
183
|
+
on_click={() => on_select_row(coords[0])}
|
|
184
|
+
/>
|
|
185
|
+
{/if}
|
|
130
186
|
|
|
131
187
|
<style>
|
|
188
|
+
.dragging {
|
|
189
|
+
cursor: crosshair !important;
|
|
190
|
+
}
|
|
191
|
+
|
|
132
192
|
input {
|
|
133
193
|
position: absolute;
|
|
134
|
-
top: var(--size-2);
|
|
135
|
-
right: var(--size-2);
|
|
136
|
-
bottom: var(--size-2);
|
|
137
|
-
left: var(--size-2);
|
|
138
194
|
flex: 1 1 0%;
|
|
139
195
|
transform: translateX(-0.1px);
|
|
140
196
|
outline: none;
|
|
141
197
|
border: none;
|
|
142
198
|
background: transparent;
|
|
143
199
|
cursor: text;
|
|
200
|
+
width: calc(100% - var(--size-2));
|
|
144
201
|
}
|
|
145
202
|
|
|
146
203
|
span {
|
|
@@ -155,25 +212,37 @@
|
|
|
155
212
|
cursor: text;
|
|
156
213
|
width: 100%;
|
|
157
214
|
height: 100%;
|
|
215
|
+
overflow: hidden;
|
|
216
|
+
text-overflow: ellipsis;
|
|
217
|
+
white-space: nowrap;
|
|
158
218
|
}
|
|
159
219
|
|
|
160
|
-
span.expanded {
|
|
220
|
+
span.text:not(.expanded) {
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
text-overflow: ellipsis;
|
|
223
|
+
white-space: nowrap;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
span.text.expanded {
|
|
161
227
|
height: auto;
|
|
162
228
|
min-height: 100%;
|
|
163
229
|
white-space: pre-wrap;
|
|
164
230
|
word-break: break-word;
|
|
165
|
-
|
|
231
|
+
overflow: visible;
|
|
166
232
|
}
|
|
167
233
|
|
|
168
234
|
.multiline {
|
|
169
|
-
white-space: pre
|
|
235
|
+
white-space: pre;
|
|
236
|
+
overflow: hidden;
|
|
237
|
+
text-overflow: ellipsis;
|
|
170
238
|
}
|
|
171
239
|
|
|
172
240
|
.header {
|
|
173
241
|
transform: translateX(0);
|
|
174
242
|
font-weight: var(--weight-bold);
|
|
175
|
-
white-space:
|
|
176
|
-
|
|
243
|
+
white-space: nowrap;
|
|
244
|
+
overflow: hidden;
|
|
245
|
+
text-overflow: ellipsis;
|
|
177
246
|
margin-left: var(--size-1);
|
|
178
247
|
}
|
|
179
248
|
|
|
@@ -181,4 +250,14 @@
|
|
|
181
250
|
opacity: 0;
|
|
182
251
|
pointer-events: none;
|
|
183
252
|
}
|
|
253
|
+
|
|
254
|
+
span :global(img) {
|
|
255
|
+
max-height: 100px;
|
|
256
|
+
width: auto;
|
|
257
|
+
object-fit: contain;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
input:read-only {
|
|
261
|
+
cursor: not-allowed;
|
|
262
|
+
}
|
|
184
263
|
</style>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let on_click: () => void;
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<button class="add-row-button" on:click={on_click} aria-label="Add row">
|
|
6
|
+
+
|
|
7
|
+
</button>
|
|
8
|
+
|
|
9
|
+
<style>
|
|
10
|
+
.add-row-button {
|
|
11
|
+
width: 100%;
|
|
12
|
+
padding: var(--size-1);
|
|
13
|
+
background: transparent;
|
|
14
|
+
border: 1px dashed var(--border-color-primary);
|
|
15
|
+
border-radius: var(--radius-sm);
|
|
16
|
+
color: var(--body-text-color);
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
transition: all 150ms;
|
|
19
|
+
margin-top: var(--size-2);
|
|
20
|
+
z-index: 10;
|
|
21
|
+
position: relative;
|
|
22
|
+
pointer-events: auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.add-row-button:hover {
|
|
26
|
+
background: var(--background-fill-secondary);
|
|
27
|
+
border-style: solid;
|
|
28
|
+
}
|
|
29
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export let index: number | null = null;
|
|
3
|
+
export let is_header = false;
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
{#if is_header}
|
|
7
|
+
<th tabindex="-1" class="row-number">
|
|
8
|
+
<div class="cell-wrap">
|
|
9
|
+
<div class="header-content">
|
|
10
|
+
<div class="header-text"></div>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</th>
|
|
14
|
+
{:else}
|
|
15
|
+
<td class="row-number" tabindex="-1" data-row={index} data-col="row-number">
|
|
16
|
+
{index !== null ? index + 1 : ""}
|
|
17
|
+
</td>
|
|
18
|
+
{/if}
|
|
19
|
+
|
|
20
|
+
<style>
|
|
21
|
+
.row-number {
|
|
22
|
+
text-align: center;
|
|
23
|
+
padding: var(--size-1);
|
|
24
|
+
min-width: var(--size-12);
|
|
25
|
+
width: var(--size-12);
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
text-overflow: ellipsis;
|
|
28
|
+
white-space: nowrap;
|
|
29
|
+
font-weight: var(--weight-semibold);
|
|
30
|
+
background: var(--table-even-background-fill);
|
|
31
|
+
border-right: 1px solid var(--border-color-primary);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:global(tr:nth-child(odd)) .row-number {
|
|
35
|
+
background: var(--table-odd-background-fill);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
:global(tr:nth-child(even)) .row-number {
|
|
39
|
+
background: var(--table-even-background-fill);
|
|
40
|
+
}
|
|
41
|
+
</style>
|