@gradio/dataframe 0.21.7 → 0.23.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 +27 -0
- package/Dataframe.test.ts +697 -32
- package/Index.svelte +107 -106
- package/dist/Index.svelte +107 -106
- package/dist/Index.svelte.d.ts +0 -1
- package/dist/shared/BooleanCell.svelte +12 -6
- package/dist/shared/BooleanCell.svelte.d.ts +4 -18
- package/dist/shared/CellMenu.svelte +74 -49
- package/dist/shared/CellMenu.svelte.d.ts +5 -19
- package/dist/shared/CellMenuButton.svelte +3 -3
- package/dist/shared/CellMenuButton.svelte.d.ts +4 -18
- package/dist/shared/CellMenuIcons.svelte +1 -1
- package/dist/shared/CellMenuIcons.svelte.d.ts +4 -18
- package/dist/shared/DataCell.svelte +189 -0
- package/dist/shared/DataCell.svelte.d.ts +44 -0
- package/dist/shared/EditableCell.svelte +88 -64
- package/dist/shared/EditableCell.svelte.d.ts +8 -26
- package/dist/shared/EmptyRowButton.svelte +2 -2
- package/dist/shared/EmptyRowButton.svelte.d.ts +4 -18
- package/dist/shared/FilterMenu.svelte +26 -18
- package/dist/shared/FilterMenu.svelte.d.ts +5 -21
- package/dist/shared/HeaderCell.svelte +218 -0
- package/dist/shared/HeaderCell.svelte.d.ts +30 -0
- package/dist/shared/RowNumber.svelte +4 -2
- package/dist/shared/RowNumber.svelte.d.ts +4 -18
- package/dist/shared/Table.svelte +1039 -947
- package/dist/shared/Table.svelte.d.ts +12 -30
- package/dist/shared/Toolbar.svelte +32 -22
- package/dist/shared/Toolbar.svelte.d.ts +6 -21
- package/dist/shared/column_measurement.svelte.d.ts +20 -0
- package/dist/shared/column_measurement.svelte.js +79 -0
- package/dist/shared/icons/FilterIcon.svelte +14 -6
- package/dist/shared/icons/FilterIcon.svelte.d.ts +14 -6
- package/dist/shared/icons/Padlock.svelte +6 -2
- package/dist/shared/icons/Padlock.svelte.d.ts +4 -25
- package/dist/shared/icons/SelectionButtons.svelte +23 -8
- package/dist/shared/icons/SelectionButtons.svelte.d.ts +4 -18
- package/dist/shared/icons/SortButtonDown.svelte +15 -4
- package/dist/shared/icons/SortButtonDown.svelte.d.ts +4 -25
- package/dist/shared/icons/SortButtonUp.svelte +15 -5
- package/dist/shared/icons/SortButtonUp.svelte.d.ts +4 -25
- package/dist/shared/icons/SortIcon.svelte +13 -7
- package/dist/shared/icons/SortIcon.svelte.d.ts +7 -21
- package/dist/shared/tanstack/index.d.ts +3 -0
- package/dist/shared/tanstack/index.js +3 -0
- package/dist/shared/tanstack/table.svelte.d.ts +26 -0
- package/dist/shared/tanstack/table.svelte.js +102 -0
- package/dist/shared/tanstack/virtual.svelte.d.ts +14 -0
- package/dist/shared/tanstack/virtual.svelte.js +76 -0
- package/dist/shared/types.d.ts +2 -0
- package/dist/shared/utils/filter.d.ts +5 -0
- package/dist/shared/utils/filter.js +62 -0
- package/dist/shared/utils/selection_utils.js +1 -1
- package/dist/shared/utils/table_utils.d.ts +1 -14
- package/dist/shared/utils/table_utils.js +0 -61
- package/dist/standalone/Index.svelte +71 -46
- package/dist/standalone/Index.svelte.d.ts +4 -23
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -0
- package/package.json +12 -11
- package/shared/BooleanCell.svelte +12 -6
- package/shared/CellMenu.svelte +74 -49
- package/shared/CellMenuButton.svelte +3 -3
- package/shared/CellMenuIcons.svelte +1 -1
- package/shared/DataCell.svelte +189 -0
- package/shared/EditableCell.svelte +88 -64
- package/shared/EmptyRowButton.svelte +2 -2
- package/shared/FilterMenu.svelte +26 -18
- package/shared/HeaderCell.svelte +218 -0
- package/shared/RowNumber.svelte +4 -2
- package/shared/Table.svelte +1039 -947
- package/shared/Toolbar.svelte +32 -22
- package/shared/column_measurement.svelte.ts +103 -0
- package/shared/icons/FilterIcon.svelte +14 -6
- package/shared/icons/Padlock.svelte +6 -2
- package/shared/icons/SelectionButtons.svelte +23 -8
- package/shared/icons/SortButtonDown.svelte +15 -4
- package/shared/icons/SortButtonUp.svelte +15 -5
- package/shared/icons/SortIcon.svelte +13 -7
- package/shared/tanstack/index.ts +23 -0
- package/shared/tanstack/table.svelte.ts +117 -0
- package/shared/tanstack/virtual.svelte.ts +105 -0
- package/shared/types.ts +3 -0
- package/shared/utils/filter.ts +66 -0
- package/shared/utils/selection_utils.ts +1 -1
- package/shared/utils/table_utils.ts +1 -100
- package/standalone/Index.svelte +71 -46
- package/test/filter.test.ts +350 -0
- package/test/selection_utils.test.ts +416 -0
- package/test/table_utils.test.ts +45 -112
- package/types.ts +34 -0
- package/dist/shared/TableCell.svelte +0 -330
- package/dist/shared/TableCell.svelte.d.ts +0 -66
- package/dist/shared/TableHeader.svelte +0 -344
- package/dist/shared/TableHeader.svelte.d.ts +0 -61
- package/dist/shared/VirtualTable.svelte +0 -379
- package/dist/shared/VirtualTable.svelte.d.ts +0 -42
- package/dist/shared/context/dataframe_context.d.ts +0 -180
- package/dist/shared/context/dataframe_context.js +0 -412
- package/dist/shared/icons/SortArrowDown.svelte +0 -25
- package/dist/shared/icons/SortArrowDown.svelte.d.ts +0 -20
- package/dist/shared/icons/SortArrowUp.svelte +0 -25
- package/dist/shared/icons/SortArrowUp.svelte.d.ts +0 -20
- package/dist/shared/utils/data_processing.d.ts +0 -14
- package/dist/shared/utils/data_processing.js +0 -47
- package/dist/shared/utils/drag_utils.d.ts +0 -15
- package/dist/shared/utils/drag_utils.js +0 -67
- package/dist/shared/utils/filter_utils.d.ts +0 -29
- package/dist/shared/utils/filter_utils.js +0 -123
- package/dist/shared/utils/keyboard_utils.d.ts +0 -3
- package/dist/shared/utils/keyboard_utils.js +0 -236
- package/dist/shared/utils/menu_utils.d.ts +0 -42
- package/dist/shared/utils/menu_utils.js +0 -58
- package/dist/shared/utils/sort_utils.d.ts +0 -26
- package/dist/shared/utils/sort_utils.js +0 -59
- package/shared/TableCell.svelte +0 -330
- package/shared/TableHeader.svelte +0 -344
- package/shared/VirtualTable.svelte +0 -379
- package/shared/context/dataframe_context.ts +0 -701
- package/shared/icons/SortArrowDown.svelte +0 -25
- package/shared/icons/SortArrowUp.svelte +0 -25
- package/shared/utils/data_processing.ts +0 -77
- package/shared/utils/drag_utils.ts +0 -105
- package/shared/utils/filter_utils.ts +0 -208
- package/shared/utils/keyboard_utils.ts +0 -309
- package/shared/utils/menu_utils.ts +0 -115
- package/shared/utils/sort_utils.ts +0 -91
- package/test/sort_utils.test.ts +0 -323
package/dist/shared/Table.svelte
CHANGED
|
@@ -1,933 +1,839 @@
|
|
|
1
|
-
<script lang="ts" context="module">
|
|
2
|
-
import {
|
|
3
|
-
create_dataframe_context,
|
|
4
|
-
type SortDirection,
|
|
5
|
-
type FilterDatatype
|
|
6
|
-
} from "./context/dataframe_context";
|
|
7
|
-
</script>
|
|
8
|
-
|
|
9
1
|
<script lang="ts">
|
|
10
|
-
import {
|
|
11
|
-
|
|
2
|
+
import {
|
|
3
|
+
createSvelteTable,
|
|
4
|
+
createSvelteVirtualizer,
|
|
5
|
+
getCoreRowModel,
|
|
6
|
+
getSortedRowModel,
|
|
7
|
+
getFilteredRowModel,
|
|
8
|
+
type ColumnDef,
|
|
9
|
+
type SortingState,
|
|
10
|
+
type ColumnFiltersState,
|
|
11
|
+
type ColumnPinningState
|
|
12
|
+
} from "./tanstack/index.js";
|
|
13
|
+
import { tick, onMount } from "svelte";
|
|
12
14
|
import { Upload } from "@gradio/upload";
|
|
13
15
|
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import TableHeader from "./TableHeader.svelte";
|
|
17
|
-
import TableCell from "./TableCell.svelte";
|
|
16
|
+
import HeaderCell from "./HeaderCell.svelte";
|
|
17
|
+
import DataCell from "./DataCell.svelte";
|
|
18
18
|
import EmptyRowButton from "./EmptyRowButton.svelte";
|
|
19
19
|
import type { SelectData } from "@gradio/utils";
|
|
20
20
|
import type { I18nFormatter } from "js/core/src/gradio_helper";
|
|
21
21
|
import { type Client } from "@gradio/client";
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
Headers,
|
|
25
|
-
DataframeValue,
|
|
26
|
-
Datatype,
|
|
27
|
-
EditData
|
|
28
|
-
} from "./utils/utils";
|
|
22
|
+
import type { Datatype, DataframeValue, EditData } from "./utils/utils";
|
|
23
|
+
import { cast_value_to_type } from "./utils/utils";
|
|
29
24
|
import CellMenu from "./CellMenu.svelte";
|
|
30
25
|
import Toolbar from "./Toolbar.svelte";
|
|
31
|
-
import type {
|
|
26
|
+
import type {
|
|
27
|
+
CellValue,
|
|
28
|
+
CellCoordinate,
|
|
29
|
+
SortDirection,
|
|
30
|
+
FilterDatatype
|
|
31
|
+
} from "./types";
|
|
32
32
|
import {
|
|
33
|
+
is_cell_in_selection,
|
|
33
34
|
is_cell_selected,
|
|
34
|
-
|
|
35
|
-
get_current_indices,
|
|
36
|
-
handle_click_outside as handle_click_outside_util,
|
|
37
|
-
calculate_selection_positions
|
|
35
|
+
handle_click_outside as handle_click_outside_util
|
|
38
36
|
} from "./utils/selection_utils";
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
max_chars
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
df_ctx.get_row = get_row;
|
|
125
|
-
df_ctx.dispatch = dispatch;
|
|
126
|
-
init_drag_handlers();
|
|
127
|
-
|
|
128
|
-
const observer = new IntersectionObserver((entries) => {
|
|
129
|
-
entries.forEach((entry) => {
|
|
130
|
-
if (entry.isIntersecting && !is_visible) {
|
|
131
|
-
width_calculated = false;
|
|
132
|
-
}
|
|
133
|
-
is_visible = entry.isIntersecting;
|
|
37
|
+
import { copy_table_data, handle_file_upload } from "./utils/table_utils";
|
|
38
|
+
import { gradio_filter_fn } from "./utils/filter";
|
|
39
|
+
import { create_column_measurement } from "./column_measurement.svelte.js";
|
|
40
|
+
|
|
41
|
+
let {
|
|
42
|
+
datatype,
|
|
43
|
+
label = null,
|
|
44
|
+
show_label = true,
|
|
45
|
+
headers = $bindable([]),
|
|
46
|
+
values = $bindable([]),
|
|
47
|
+
col_count,
|
|
48
|
+
row_count,
|
|
49
|
+
latex_delimiters,
|
|
50
|
+
components = {},
|
|
51
|
+
editable = true,
|
|
52
|
+
wrap = false,
|
|
53
|
+
root,
|
|
54
|
+
i18n,
|
|
55
|
+
max_height = 500,
|
|
56
|
+
line_breaks = true,
|
|
57
|
+
column_widths = [],
|
|
58
|
+
show_row_numbers = false,
|
|
59
|
+
upload,
|
|
60
|
+
stream_handler,
|
|
61
|
+
buttons = null,
|
|
62
|
+
value_is_output = $bindable(false),
|
|
63
|
+
max_chars = undefined,
|
|
64
|
+
show_search = "none",
|
|
65
|
+
pinned_columns = 0,
|
|
66
|
+
static_columns = [],
|
|
67
|
+
fullscreen = false,
|
|
68
|
+
display_value = null,
|
|
69
|
+
styling = null,
|
|
70
|
+
onchange,
|
|
71
|
+
oninput,
|
|
72
|
+
onselect,
|
|
73
|
+
onedit,
|
|
74
|
+
onsearch,
|
|
75
|
+
onfullscreen
|
|
76
|
+
}: {
|
|
77
|
+
datatype: Datatype | Datatype[];
|
|
78
|
+
label?: string | null;
|
|
79
|
+
show_label?: boolean;
|
|
80
|
+
headers?: (string | null)[];
|
|
81
|
+
values?: CellValue[][];
|
|
82
|
+
col_count: [number, "fixed" | "dynamic"];
|
|
83
|
+
row_count: [number, "fixed" | "dynamic"];
|
|
84
|
+
latex_delimiters: { left: string; right: string; display: boolean }[];
|
|
85
|
+
components?: Record<string, any>;
|
|
86
|
+
editable?: boolean;
|
|
87
|
+
wrap?: boolean;
|
|
88
|
+
root: string;
|
|
89
|
+
i18n: I18nFormatter;
|
|
90
|
+
max_height?: number;
|
|
91
|
+
line_breaks?: boolean;
|
|
92
|
+
column_widths?: string[];
|
|
93
|
+
show_row_numbers?: boolean;
|
|
94
|
+
upload: Client["upload"];
|
|
95
|
+
stream_handler: Client["stream"];
|
|
96
|
+
buttons?: string[] | null;
|
|
97
|
+
value_is_output?: boolean;
|
|
98
|
+
max_chars?: number | undefined;
|
|
99
|
+
show_search?: "none" | "search" | "filter";
|
|
100
|
+
pinned_columns?: number;
|
|
101
|
+
static_columns?: (string | number)[];
|
|
102
|
+
fullscreen?: boolean;
|
|
103
|
+
display_value?: string[][] | null;
|
|
104
|
+
styling?: string[][] | null;
|
|
105
|
+
onchange?: (detail: DataframeValue) => void;
|
|
106
|
+
oninput?: () => void;
|
|
107
|
+
onselect?: (detail: SelectData) => void;
|
|
108
|
+
onedit?: (detail: EditData) => void;
|
|
109
|
+
onsearch?: (detail: string | null) => void;
|
|
110
|
+
onfullscreen?: () => void;
|
|
111
|
+
} = $props();
|
|
112
|
+
|
|
113
|
+
type GradioRow = Record<string, CellValue> & { _index: number };
|
|
114
|
+
|
|
115
|
+
// convert values[][] into tanstack row objects
|
|
116
|
+
let row_data: GradioRow[] = $derived(
|
|
117
|
+
(values ?? []).map((row, i) => {
|
|
118
|
+
const obj: GradioRow = { _index: i };
|
|
119
|
+
(row ?? []).forEach((val, j) => {
|
|
120
|
+
const dtype = Array.isArray(datatype) ? datatype[j] : datatype;
|
|
121
|
+
obj[`col_${j}`] = cast_value_to_type(val, dtype);
|
|
134
122
|
});
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
123
|
+
return obj;
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
let resolved_headers = $derived.by(() => {
|
|
128
|
+
let h = headers ?? [];
|
|
129
|
+
if (col_count[1] === "fixed" && h.length < col_count[0]) {
|
|
130
|
+
h = [
|
|
131
|
+
...h,
|
|
132
|
+
...Array(col_count[0] - h.length)
|
|
133
|
+
.fill(null)
|
|
134
|
+
.map((_, i) => `${i + h.length}`)
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
if (!h.length) {
|
|
138
|
+
h = Array(col_count[0])
|
|
139
|
+
.fill(null)
|
|
140
|
+
.map((_, i) => `${i}`);
|
|
141
|
+
}
|
|
142
|
+
return h.map((v) => v ?? "");
|
|
143
|
+
});
|
|
139
144
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
let column_defs: ColumnDef<GradioRow, CellValue>[] = $derived(
|
|
146
|
+
resolved_headers.map((header_value, j) => ({
|
|
147
|
+
id: `col_${j}`,
|
|
148
|
+
accessorKey: `col_${j}`,
|
|
149
|
+
header: header_value,
|
|
150
|
+
size: column_widths[j] ? parseInt(column_widths[j]) || 150 : 150,
|
|
151
|
+
minSize: 45,
|
|
152
|
+
filterFn: gradio_filter_fn,
|
|
153
|
+
meta: {
|
|
154
|
+
colIndex: j,
|
|
155
|
+
datatype: Array.isArray(datatype) ? datatype[j] : datatype,
|
|
156
|
+
isStatic:
|
|
157
|
+
static_columns.includes(j) || static_columns.includes(header_value),
|
|
158
|
+
isPinned: j < pinned_columns
|
|
143
159
|
}
|
|
144
|
-
}
|
|
145
|
-
|
|
160
|
+
}))
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
let sorting: SortingState = $state([]);
|
|
164
|
+
let column_filters: ColumnFiltersState = $state([]);
|
|
165
|
+
let global_filter: string = $state("");
|
|
166
|
+
let column_pinning: ColumnPinningState = $derived({
|
|
167
|
+
left: column_defs.filter((_, i) => i < pinned_columns).map((c) => c.id!)
|
|
168
|
+
});
|
|
146
169
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
170
|
+
const table = createSvelteTable<GradioRow>({
|
|
171
|
+
get data() {
|
|
172
|
+
return row_data;
|
|
173
|
+
},
|
|
174
|
+
get columns() {
|
|
175
|
+
return column_defs;
|
|
176
|
+
},
|
|
177
|
+
state: {
|
|
178
|
+
get sorting() {
|
|
179
|
+
return sorting;
|
|
180
|
+
},
|
|
181
|
+
get columnFilters() {
|
|
182
|
+
return column_filters;
|
|
183
|
+
},
|
|
184
|
+
get globalFilter() {
|
|
185
|
+
return global_filter;
|
|
186
|
+
},
|
|
187
|
+
get columnPinning() {
|
|
188
|
+
return column_pinning;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
onSortingChange: (updater) => {
|
|
192
|
+
sorting = typeof updater === "function" ? updater(sorting) : updater;
|
|
193
|
+
},
|
|
194
|
+
onColumnFiltersChange: (updater) => {
|
|
195
|
+
column_filters =
|
|
196
|
+
typeof updater === "function" ? updater(column_filters) : updater;
|
|
197
|
+
},
|
|
198
|
+
onGlobalFilterChange: (updater) => {
|
|
199
|
+
global_filter =
|
|
200
|
+
typeof updater === "function" ? updater(global_filter) : updater;
|
|
201
|
+
},
|
|
202
|
+
getCoreRowModel: getCoreRowModel(),
|
|
203
|
+
getSortedRowModel: getSortedRowModel(),
|
|
204
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
205
|
+
globalFilterFn: "includesString",
|
|
206
|
+
enableSorting: true,
|
|
207
|
+
enableMultiSort: true,
|
|
208
|
+
maxMultiSortColCount: 3
|
|
153
209
|
});
|
|
154
210
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
211
|
+
let rows = $derived(table.getRowModel().rows);
|
|
212
|
+
let header_groups = $derived(table.getHeaderGroups());
|
|
213
|
+
|
|
214
|
+
let scroll_container: HTMLDivElement;
|
|
215
|
+
|
|
216
|
+
const virtualizer = createSvelteVirtualizer<
|
|
217
|
+
HTMLDivElement,
|
|
218
|
+
HTMLTableRowElement
|
|
219
|
+
>({
|
|
220
|
+
get count() {
|
|
221
|
+
return rows.length;
|
|
222
|
+
},
|
|
223
|
+
getScrollElement: () => scroll_container,
|
|
224
|
+
estimateSize: () => 35,
|
|
225
|
+
overscan: 10,
|
|
226
|
+
measureElement: (el, _entry, instance) => {
|
|
227
|
+
const h = el.getBoundingClientRect().height;
|
|
228
|
+
if (h > 0) return h;
|
|
229
|
+
|
|
230
|
+
const idx = el.getAttribute("data-index");
|
|
231
|
+
if (idx != null) {
|
|
232
|
+
const cached = (instance as any).itemSizeCache?.get(Number(idx));
|
|
233
|
+
if (typeof cached === "number") return cached;
|
|
234
|
+
}
|
|
235
|
+
return 35;
|
|
162
236
|
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const dispatch = createEventDispatcher<{
|
|
166
|
-
change: DataframeValue;
|
|
167
|
-
input: undefined;
|
|
168
|
-
select: SelectData;
|
|
169
|
-
search: string | null;
|
|
170
|
-
edit: EditData;
|
|
171
|
-
}>();
|
|
172
|
-
|
|
173
|
-
let els: Record<
|
|
174
|
-
string,
|
|
175
|
-
{ cell: null | HTMLTableCellElement; input: null | HTMLTextAreaElement }
|
|
176
|
-
> = {};
|
|
177
|
-
let data_binding: Record<string, (typeof data)[0][0]> = {};
|
|
178
|
-
let _headers = make_headers(headers, col_count, els, make_id);
|
|
179
|
-
let old_headers: string[] = headers;
|
|
180
|
-
let data: { id: string; value: CellValue; display_value?: string }[][] = [[]];
|
|
181
|
-
let old_val: undefined | CellValue[][] = undefined;
|
|
182
|
-
let search_results: {
|
|
183
|
-
id: string;
|
|
184
|
-
value: CellValue;
|
|
185
|
-
display_value?: string;
|
|
186
|
-
styling?: string;
|
|
187
|
-
}[][] = [[]];
|
|
188
|
-
let dragging = false;
|
|
189
|
-
let color_accent_copied: string;
|
|
190
|
-
let filtered_to_original_map: number[] = [];
|
|
191
|
-
|
|
192
|
-
onMount(() => {
|
|
193
|
-
const color = getComputedStyle(document.documentElement)
|
|
194
|
-
.getPropertyValue("--color-accent")
|
|
195
|
-
.trim();
|
|
196
|
-
color_accent_copied = color + "40"; // 80 is 50% opacity in hex
|
|
197
|
-
document.documentElement.style.setProperty(
|
|
198
|
-
"--color-accent-copied",
|
|
199
|
-
color_accent_copied
|
|
200
|
-
);
|
|
201
237
|
});
|
|
202
238
|
|
|
203
|
-
|
|
204
|
-
|
|
239
|
+
let virtual_items = $derived(virtualizer.virtualItems());
|
|
240
|
+
let total_size = $derived(virtualizer.totalSize());
|
|
241
|
+
|
|
242
|
+
let selected_cells: CellCoordinate[] = $state([]);
|
|
243
|
+
let selected: CellCoordinate | false = $state(false);
|
|
244
|
+
let editing: CellCoordinate | false = $state(false);
|
|
245
|
+
let header_edit: number | false = $state(false);
|
|
246
|
+
let selected_header: number | false = $state(false);
|
|
247
|
+
let active_cell_menu: {
|
|
248
|
+
row: number;
|
|
249
|
+
col: number;
|
|
250
|
+
x: number;
|
|
251
|
+
y: number;
|
|
252
|
+
} | null = $state(null);
|
|
253
|
+
let active_header_menu: { col: number; x: number; y: number } | null =
|
|
254
|
+
$state(null);
|
|
255
|
+
let copy_flash = $state(false);
|
|
256
|
+
let is_dragging = $state(false);
|
|
257
|
+
let show_scroll_button = $state(false);
|
|
258
|
+
let dragging = $state(false); // file drag
|
|
205
259
|
|
|
206
|
-
|
|
207
|
-
data?.map((row) => row[col]?.value) ?? [];
|
|
260
|
+
let parent: HTMLDivElement;
|
|
208
261
|
|
|
209
|
-
|
|
210
|
-
|
|
262
|
+
function get_dtype(col: number): Datatype {
|
|
263
|
+
return Array.isArray(datatype) ? (datatype[col] ?? "str") : datatype;
|
|
264
|
+
}
|
|
211
265
|
|
|
212
|
-
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
266
|
+
function get_display_value(row: number, col: number): string {
|
|
267
|
+
if (display_value?.[row]?.[col] !== undefined)
|
|
268
|
+
return display_value[row][col];
|
|
269
|
+
return String(values?.[row]?.[col] ?? "");
|
|
217
270
|
}
|
|
218
271
|
|
|
219
|
-
function
|
|
220
|
-
return
|
|
272
|
+
function get_styling(row: number, col: number): string {
|
|
273
|
+
return styling?.[row]?.[col] ?? "";
|
|
221
274
|
}
|
|
222
275
|
|
|
223
|
-
|
|
224
|
-
|
|
276
|
+
function push_change(
|
|
277
|
+
new_values?: CellValue[][],
|
|
278
|
+
new_headers?: (string | null)[]
|
|
279
|
+
): void {
|
|
280
|
+
onchange?.({
|
|
281
|
+
data: new_values ?? values,
|
|
282
|
+
headers: (new_headers ?? resolved_headers) as string[],
|
|
283
|
+
metadata: null
|
|
284
|
+
});
|
|
285
|
+
if (!value_is_output) oninput?.();
|
|
286
|
+
value_is_output = false;
|
|
287
|
+
}
|
|
225
288
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
(values.length !== old_val.length ||
|
|
234
|
-
(values[0] && old_val[0] && values[0].length !== old_val[0].length));
|
|
289
|
+
function handle_cell_click(
|
|
290
|
+
event: MouseEvent,
|
|
291
|
+
row: number,
|
|
292
|
+
col: number
|
|
293
|
+
): void {
|
|
294
|
+
event.preventDefault();
|
|
295
|
+
event.stopPropagation();
|
|
235
296
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
297
|
+
const coord: CellCoordinate = [row, col];
|
|
298
|
+
if (event.shiftKey && selected) {
|
|
299
|
+
// range select
|
|
300
|
+
const [r1, c1] = selected;
|
|
301
|
+
const [r2, c2] = coord;
|
|
302
|
+
const new_cells: CellCoordinate[] = [];
|
|
303
|
+
for (let r = Math.min(r1, r2); r <= Math.max(r1, r2); r++) {
|
|
304
|
+
for (let c = Math.min(c1, c2); c <= Math.max(c1, c2); c++) {
|
|
305
|
+
new_cells.push([r, c]);
|
|
239
306
|
}
|
|
240
|
-
last_width_data_length = 0;
|
|
241
|
-
last_width_column_count = 0;
|
|
242
|
-
width_calculated = false;
|
|
243
307
|
}
|
|
308
|
+
selected_cells = new_cells;
|
|
309
|
+
} else if (event.metaKey || event.ctrlKey) {
|
|
310
|
+
// toggle select
|
|
311
|
+
const exists = selected_cells.some(([r, c]) => r === row && c === col);
|
|
312
|
+
selected_cells = exists
|
|
313
|
+
? selected_cells.filter(([r, c]) => !(r === row && c === col))
|
|
314
|
+
: [...selected_cells, coord];
|
|
315
|
+
} else {
|
|
316
|
+
selected_cells = [coord];
|
|
244
317
|
}
|
|
245
318
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
(values.length !== old_val.length ||
|
|
252
|
-
(values[0] && old_val[0] && values[0].length !== old_val[0].length));
|
|
253
|
-
|
|
254
|
-
data = process_data(
|
|
255
|
-
values as CellValue[][],
|
|
256
|
-
els,
|
|
257
|
-
data_binding,
|
|
258
|
-
make_id,
|
|
259
|
-
display_value,
|
|
260
|
-
datatype
|
|
261
|
-
);
|
|
262
|
-
old_val = JSON.parse(JSON.stringify(values)) as CellValue[][];
|
|
319
|
+
selected = coord;
|
|
320
|
+
header_edit = false;
|
|
321
|
+
selected_header = false;
|
|
322
|
+
active_cell_menu = null;
|
|
323
|
+
active_header_menu = null;
|
|
263
324
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
} else if ($df_state.sort_state.sort_columns.length > 0) {
|
|
267
|
-
sort_data(data, display_value, styling);
|
|
268
|
-
} else {
|
|
269
|
-
df_actions.handle_sort(-1, "asc");
|
|
270
|
-
df_actions.reset_sort_state();
|
|
271
|
-
}
|
|
325
|
+
// click selects, does NOT enter edit mode (double-click or typing does)
|
|
326
|
+
editing = false;
|
|
272
327
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
328
|
+
onselect?.({
|
|
329
|
+
index: coord,
|
|
330
|
+
value: values?.[row]?.[col],
|
|
331
|
+
row_value: values?.[row] ?? [],
|
|
332
|
+
col_value: values?.map((r) => r[col]) ?? []
|
|
333
|
+
} as any);
|
|
278
334
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
335
|
+
tick().then(() => parent?.focus());
|
|
336
|
+
}
|
|
282
337
|
|
|
283
|
-
|
|
284
|
-
|
|
338
|
+
function handle_cell_dblclick(
|
|
339
|
+
event: MouseEvent,
|
|
340
|
+
row: number,
|
|
341
|
+
col: number
|
|
342
|
+
): void {
|
|
343
|
+
event.preventDefault();
|
|
344
|
+
event.stopPropagation();
|
|
345
|
+
if (!editable) return;
|
|
346
|
+
const col_is_static =
|
|
347
|
+
static_columns.includes(col) ||
|
|
348
|
+
static_columns.includes(resolved_headers[col]);
|
|
349
|
+
if (!col_is_static) {
|
|
350
|
+
editing = [row, col];
|
|
285
351
|
}
|
|
286
352
|
}
|
|
287
353
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
row
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
styling: styling?.[row_idx]?.[col_idx] || ""
|
|
310
|
-
});
|
|
354
|
+
function handle_blur(detail: {
|
|
355
|
+
blur_event: FocusEvent;
|
|
356
|
+
coords: [number, number];
|
|
357
|
+
}): void {
|
|
358
|
+
const { coords } = detail;
|
|
359
|
+
const input_el = detail.blur_event.target as HTMLTextAreaElement;
|
|
360
|
+
if (!input_el || input_el.value === undefined) return;
|
|
361
|
+
|
|
362
|
+
const [row, col] = coords;
|
|
363
|
+
const old_value = values?.[row]?.[col];
|
|
364
|
+
const new_value = input_el.value;
|
|
365
|
+
|
|
366
|
+
if (String(old_value) !== String(new_value)) {
|
|
367
|
+
const new_values = values.map((r) => [...r]);
|
|
368
|
+
new_values[row][col] = new_value;
|
|
369
|
+
values = new_values;
|
|
370
|
+
|
|
371
|
+
onedit?.({
|
|
372
|
+
index: [row, col],
|
|
373
|
+
value: new_value,
|
|
374
|
+
previous_value: String(old_value ?? "")
|
|
311
375
|
});
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const filtered = df_actions.filter_data(data);
|
|
315
|
-
|
|
316
|
-
search_results = filtered.map((row) =>
|
|
317
|
-
row.map((cell) => {
|
|
318
|
-
const original = cell_map.get(cell.id);
|
|
319
|
-
return {
|
|
320
|
-
...cell,
|
|
321
|
-
display_value:
|
|
322
|
-
original?.display_value !== undefined
|
|
323
|
-
? original.display_value
|
|
324
|
-
: String(cell.value),
|
|
325
|
-
styling: original?.styling || ""
|
|
326
|
-
};
|
|
327
|
-
})
|
|
328
|
-
);
|
|
329
|
-
} else {
|
|
330
|
-
filtered_to_original_map = [];
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
let previous_headers = _headers.map((h) => h.value);
|
|
334
|
-
let previous_data = data.map((row) => row.map((cell) => cell.value));
|
|
335
|
-
|
|
336
|
-
$: {
|
|
337
|
-
if (data || _headers) {
|
|
338
|
-
df_actions.trigger_change(
|
|
339
|
-
data,
|
|
340
|
-
_headers,
|
|
341
|
-
previous_data,
|
|
342
|
-
previous_headers,
|
|
343
|
-
value_is_output,
|
|
344
|
-
dispatch
|
|
345
|
-
);
|
|
346
|
-
previous_data = data.map((row) => row.map((cell) => cell.value));
|
|
347
|
-
previous_headers = _headers.map((h) => h.value);
|
|
376
|
+
push_change(new_values);
|
|
348
377
|
}
|
|
349
378
|
}
|
|
350
379
|
|
|
351
|
-
function
|
|
352
|
-
|
|
353
|
-
|
|
380
|
+
function handle_header_click(event: MouseEvent, col: number): void {
|
|
381
|
+
if (event.target instanceof HTMLAnchorElement) return;
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
event.stopPropagation();
|
|
384
|
+
if (!editable) return;
|
|
385
|
+
|
|
386
|
+
editing = false;
|
|
387
|
+
selected = false;
|
|
388
|
+
selected_cells = [];
|
|
389
|
+
active_cell_menu = null;
|
|
390
|
+
active_header_menu = null;
|
|
391
|
+
selected_header = col;
|
|
392
|
+
header_edit = editable ? col : false;
|
|
393
|
+
parent?.focus();
|
|
354
394
|
}
|
|
355
395
|
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
|
|
396
|
+
function end_header_edit(key: string): void {
|
|
397
|
+
if (["Escape", "Enter", "Tab"].includes(key)) {
|
|
398
|
+
header_edit = false;
|
|
399
|
+
parent?.focus();
|
|
400
|
+
}
|
|
359
401
|
}
|
|
360
402
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
403
|
+
function toggle_header_menu(event: MouseEvent, col: number): void {
|
|
404
|
+
event.stopPropagation();
|
|
405
|
+
if (active_header_menu?.col === col) {
|
|
406
|
+
active_header_menu = null;
|
|
407
|
+
} else {
|
|
408
|
+
const th = (event.target as HTMLElement).closest("th");
|
|
409
|
+
if (th) {
|
|
410
|
+
const rect = th.getBoundingClientRect();
|
|
411
|
+
active_header_menu = { col, x: rect.right, y: rect.bottom };
|
|
412
|
+
}
|
|
364
413
|
}
|
|
414
|
+
}
|
|
365
415
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
416
|
+
function handle_sort(col: number, direction: SortDirection): void {
|
|
417
|
+
const col_id = `col_${col}`;
|
|
418
|
+
const desc = direction === "desc";
|
|
419
|
+
// if already sorted this way, remove it
|
|
420
|
+
const existing = sorting.findIndex((s) => s.id === col_id);
|
|
421
|
+
if (existing >= 0 && sorting[existing].desc === desc) {
|
|
422
|
+
sorting = sorting.filter((s) => s.id !== col_id);
|
|
423
|
+
} else {
|
|
424
|
+
sorting = [
|
|
425
|
+
...sorting.filter((s) => s.id !== col_id),
|
|
426
|
+
{ id: col_id, desc }
|
|
427
|
+
].slice(-3);
|
|
369
428
|
}
|
|
370
429
|
}
|
|
371
430
|
|
|
431
|
+
function clear_sort(): void {
|
|
432
|
+
sorting = [];
|
|
433
|
+
}
|
|
434
|
+
|
|
372
435
|
function handle_filter(
|
|
373
436
|
col: number,
|
|
374
|
-
|
|
437
|
+
dtype: FilterDatatype,
|
|
375
438
|
filter: string,
|
|
376
|
-
|
|
439
|
+
fvalue: string
|
|
377
440
|
): void {
|
|
378
|
-
|
|
379
|
-
|
|
441
|
+
const col_id = `col_${col}`;
|
|
442
|
+
const existing = column_filters.findIndex((f) => f.id === col_id);
|
|
443
|
+
if (existing >= 0) {
|
|
444
|
+
column_filters = column_filters.filter((f) => f.id !== col_id);
|
|
445
|
+
} else {
|
|
446
|
+
column_filters = [
|
|
447
|
+
...column_filters,
|
|
448
|
+
{ id: col_id, value: { dtype, filter, value: fvalue } }
|
|
449
|
+
];
|
|
450
|
+
}
|
|
380
451
|
}
|
|
381
452
|
|
|
382
453
|
function clear_filter(): void {
|
|
383
|
-
|
|
384
|
-
filter_data(data, display_value, styling);
|
|
454
|
+
column_filters = [];
|
|
385
455
|
}
|
|
386
456
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
457
|
+
function handle_search(query: string | null): void {
|
|
458
|
+
global_filter = query ?? "";
|
|
459
|
+
onsearch?.(query);
|
|
390
460
|
}
|
|
391
461
|
|
|
392
|
-
function
|
|
393
|
-
if (
|
|
394
|
-
|
|
462
|
+
function add_row(index?: number): void {
|
|
463
|
+
if (row_count[1] !== "dynamic") return;
|
|
464
|
+
const col_len = values[0]?.length || resolved_headers.length || 1;
|
|
465
|
+
const new_row: CellValue[] = Array(col_len).fill("");
|
|
466
|
+
const new_values = [...values];
|
|
467
|
+
if (index !== undefined) {
|
|
468
|
+
new_values.splice(index, 0, new_row);
|
|
469
|
+
} else {
|
|
470
|
+
new_values.push(new_row);
|
|
395
471
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
df_actions.handle_header_click(col, editable);
|
|
401
|
-
parent.focus();
|
|
472
|
+
values = new_values;
|
|
473
|
+
push_change(new_values);
|
|
474
|
+
selected = [index ?? new_values.length - 1, 0];
|
|
475
|
+
parent?.focus();
|
|
402
476
|
}
|
|
403
477
|
|
|
404
|
-
function
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const new_row = Array(data[0]?.length || headers.length)
|
|
416
|
-
.fill(0)
|
|
417
|
-
.map((_, i) => {
|
|
418
|
-
const _id = make_id();
|
|
419
|
-
els[_id] = { cell: null, input: null };
|
|
420
|
-
return { id: _id, value: "" };
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
if (data.length === 0) {
|
|
424
|
-
data = [new_row];
|
|
425
|
-
} else if (index !== undefined && index >= 0 && index <= data.length) {
|
|
426
|
-
data.splice(index, 0, new_row);
|
|
427
|
-
} else {
|
|
428
|
-
data.push(new_row);
|
|
478
|
+
function add_col(index?: number): void {
|
|
479
|
+
if (col_count[1] !== "dynamic") return;
|
|
480
|
+
const new_headers = [
|
|
481
|
+
...(headers ?? []),
|
|
482
|
+
`Header ${(headers?.length ?? 0) + 1}`
|
|
483
|
+
];
|
|
484
|
+
const new_values = values.map((row) => [...row, ""]);
|
|
485
|
+
if (index !== undefined) {
|
|
486
|
+
new_headers.splice(index, 0, new_headers.pop()!);
|
|
487
|
+
new_values.forEach((row) => row.splice(index, 0, row.pop()!));
|
|
429
488
|
}
|
|
489
|
+
values = new_values;
|
|
490
|
+
headers = new_headers;
|
|
491
|
+
push_change(new_values, new_headers);
|
|
492
|
+
parent?.focus();
|
|
493
|
+
}
|
|
430
494
|
|
|
431
|
-
|
|
495
|
+
function delete_row_at(index: number): void {
|
|
496
|
+
if (values.length <= 1) return;
|
|
497
|
+
values = [...values.slice(0, index), ...values.slice(index + 1)];
|
|
498
|
+
push_change(values);
|
|
499
|
+
active_cell_menu = null;
|
|
500
|
+
active_header_menu = null;
|
|
432
501
|
}
|
|
433
502
|
|
|
434
|
-
|
|
435
|
-
parent.focus();
|
|
503
|
+
function delete_col_at(index: number): void {
|
|
436
504
|
if (col_count[1] !== "dynamic") return;
|
|
505
|
+
if ((values[0]?.length ?? 0) <= 1) return;
|
|
506
|
+
values = values.map((row) => [
|
|
507
|
+
...row.slice(0, index),
|
|
508
|
+
...row.slice(index + 1)
|
|
509
|
+
]);
|
|
510
|
+
headers = [
|
|
511
|
+
...(headers ?? []).slice(0, index),
|
|
512
|
+
...(headers ?? []).slice(index + 1)
|
|
513
|
+
];
|
|
514
|
+
push_change(values, headers as string[]);
|
|
515
|
+
active_cell_menu = null;
|
|
516
|
+
active_header_menu = null;
|
|
517
|
+
selected = false;
|
|
518
|
+
selected_cells = [];
|
|
519
|
+
editing = false;
|
|
520
|
+
}
|
|
437
521
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (!els[cell.id]) {
|
|
443
|
-
els[cell.id] = { cell: null, input: null };
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
});
|
|
522
|
+
function add_row_at(index: number, position: "above" | "below"): void {
|
|
523
|
+
add_row(position === "above" ? index : index + 1);
|
|
524
|
+
active_cell_menu = null;
|
|
525
|
+
}
|
|
447
526
|
|
|
448
|
-
|
|
449
|
-
|
|
527
|
+
function add_col_at(index: number, position: "left" | "right"): void {
|
|
528
|
+
add_col(position === "left" ? index : index + 1);
|
|
529
|
+
active_cell_menu = null;
|
|
530
|
+
}
|
|
450
531
|
|
|
451
|
-
|
|
532
|
+
// function handle_select_all(col: number, checked: boolean): void {
|
|
533
|
+
// values = values.map((row) => {
|
|
534
|
+
// const new_row = [...row];
|
|
535
|
+
// new_row[col] = checked;
|
|
536
|
+
// return new_row;
|
|
537
|
+
// });
|
|
538
|
+
// push_change(values);
|
|
539
|
+
// }
|
|
452
540
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
541
|
+
function commit_filter(): void {
|
|
542
|
+
if (!global_filter || show_search !== "filter") return;
|
|
543
|
+
// get the filtered rows from tanstack and push as new values
|
|
544
|
+
const filtered_values = rows.map((row) => {
|
|
545
|
+
const original_idx = row.original._index;
|
|
546
|
+
return values[original_idx];
|
|
457
547
|
});
|
|
548
|
+
values = filtered_values;
|
|
549
|
+
global_filter = "";
|
|
550
|
+
push_change(filtered_values);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async function handle_copy(): Promise<void> {
|
|
554
|
+
const data_for_copy = values.map((row) =>
|
|
555
|
+
row.map((val, j) => ({ id: `${j}`, value: val }))
|
|
556
|
+
);
|
|
557
|
+
const cells_to_copy = selected_cells.length > 0 ? selected_cells : null;
|
|
558
|
+
await copy_table_data(data_for_copy, cells_to_copy);
|
|
559
|
+
copy_flash = true;
|
|
560
|
+
setTimeout(() => (copy_flash = false), 800);
|
|
458
561
|
}
|
|
459
562
|
|
|
460
563
|
function handle_click_outside(event: Event): void {
|
|
461
564
|
if (handle_click_outside_util(event, parent)) {
|
|
462
|
-
|
|
565
|
+
selected_cells = [];
|
|
566
|
+
selected = false;
|
|
567
|
+
editing = false;
|
|
463
568
|
header_edit = false;
|
|
464
569
|
selected_header = false;
|
|
570
|
+
active_cell_menu = null;
|
|
571
|
+
active_header_menu = null;
|
|
465
572
|
}
|
|
466
573
|
}
|
|
467
574
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
let width_calc_timeout: ReturnType<typeof setTimeout>;
|
|
471
|
-
$: if (cells[0] && cells[0]?.clientWidth) {
|
|
472
|
-
clearTimeout(width_calc_timeout);
|
|
473
|
-
width_calc_timeout = setTimeout(() => set_cell_widths(), 100);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
let width_calculated = false;
|
|
477
|
-
$: if (cells[0] && !width_calculated) {
|
|
478
|
-
set_cell_widths();
|
|
479
|
-
width_calculated = true;
|
|
480
|
-
}
|
|
481
|
-
let cells: HTMLTableCellElement[] = [];
|
|
482
|
-
let parent: HTMLDivElement;
|
|
483
|
-
let table: HTMLTableElement;
|
|
484
|
-
let last_width_data_length = 0;
|
|
485
|
-
let last_width_column_count = 0;
|
|
486
|
-
|
|
487
|
-
function set_cell_widths(): void {
|
|
488
|
-
const column_count = data[0]?.length || 0;
|
|
489
|
-
if ($df_state.filter_state.filter_columns.length > 0) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
if (
|
|
493
|
-
last_width_data_length === data.length &&
|
|
494
|
-
last_width_column_count === column_count &&
|
|
495
|
-
$df_state.sort_state.sort_columns.length > 0
|
|
496
|
-
) {
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
575
|
+
function handle_keydown(e: KeyboardEvent): void {
|
|
576
|
+
if (!selected && selected_header === false) return;
|
|
499
577
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
578
|
+
const num_rows = rows.length;
|
|
579
|
+
const num_cols = resolved_headers.length;
|
|
503
580
|
|
|
504
|
-
|
|
505
|
-
|
|
581
|
+
if (selected) {
|
|
582
|
+
const [row, col] = selected;
|
|
506
583
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
584
|
+
switch (e.key) {
|
|
585
|
+
case "ArrowUp":
|
|
586
|
+
e.preventDefault();
|
|
587
|
+
if (row > 0) {
|
|
588
|
+
selected = [row - 1, col];
|
|
589
|
+
selected_cells = [selected];
|
|
590
|
+
editing = false;
|
|
591
|
+
virtualizer.instance.scrollToIndex(row - 1, { align: "auto" });
|
|
592
|
+
}
|
|
593
|
+
break;
|
|
594
|
+
case "ArrowDown":
|
|
595
|
+
e.preventDefault();
|
|
596
|
+
if (row < num_rows - 1) {
|
|
597
|
+
selected = [row + 1, col];
|
|
598
|
+
selected_cells = [selected];
|
|
599
|
+
editing = false;
|
|
600
|
+
virtualizer.instance.scrollToIndex(row + 1, { align: "auto" });
|
|
601
|
+
}
|
|
602
|
+
break;
|
|
603
|
+
case "ArrowLeft":
|
|
604
|
+
e.preventDefault();
|
|
605
|
+
if (col > 0) {
|
|
606
|
+
selected = [row, col - 1];
|
|
607
|
+
selected_cells = [selected];
|
|
608
|
+
editing = false;
|
|
609
|
+
}
|
|
610
|
+
break;
|
|
611
|
+
case "ArrowRight":
|
|
612
|
+
e.preventDefault();
|
|
613
|
+
if (col < num_cols - 1) {
|
|
614
|
+
selected = [row, col + 1];
|
|
615
|
+
selected_cells = [selected];
|
|
616
|
+
editing = false;
|
|
617
|
+
}
|
|
618
|
+
break;
|
|
619
|
+
case "Tab": {
|
|
620
|
+
e.preventDefault();
|
|
621
|
+
const was_editing = !!editing;
|
|
622
|
+
if (e.shiftKey) {
|
|
623
|
+
if (col > 0) selected = [row, col - 1];
|
|
624
|
+
else if (row > 0) selected = [row - 1, num_cols - 1];
|
|
625
|
+
} else {
|
|
626
|
+
if (col < num_cols - 1) selected = [row, col + 1];
|
|
627
|
+
else if (row < num_rows - 1) selected = [row + 1, 0];
|
|
628
|
+
}
|
|
629
|
+
selected_cells = [selected];
|
|
630
|
+
if (was_editing) {
|
|
631
|
+
const tab_col = (selected as CellCoordinate)[1];
|
|
632
|
+
const tab_static =
|
|
633
|
+
static_columns.includes(tab_col) ||
|
|
634
|
+
static_columns.includes(resolved_headers[tab_col]);
|
|
635
|
+
editing = editable && !tab_static ? selected : false;
|
|
636
|
+
} else {
|
|
637
|
+
editing = false;
|
|
638
|
+
}
|
|
639
|
+
if (!editing) tick().then(() => parent?.focus());
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
case "Enter":
|
|
643
|
+
if (editing && e.shiftKey) {
|
|
644
|
+
// shift+enter inserts newline in textarea — don't intercept
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
e.preventDefault();
|
|
648
|
+
if (editing) {
|
|
649
|
+
editing = false;
|
|
650
|
+
if (row < num_rows - 1) {
|
|
651
|
+
selected = [row + 1, col];
|
|
652
|
+
selected_cells = [selected];
|
|
653
|
+
}
|
|
654
|
+
tick().then(() => parent?.focus());
|
|
655
|
+
} else if (editable) {
|
|
656
|
+
const enter_static =
|
|
657
|
+
static_columns.includes(col) ||
|
|
658
|
+
static_columns.includes(resolved_headers[col]);
|
|
659
|
+
if (!enter_static) {
|
|
660
|
+
editing = [row, col];
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
break;
|
|
664
|
+
case "Escape":
|
|
665
|
+
editing = false;
|
|
666
|
+
tick().then(() => parent?.focus());
|
|
667
|
+
break;
|
|
668
|
+
case "Delete":
|
|
669
|
+
case "Backspace":
|
|
670
|
+
if (!editing && editable) {
|
|
671
|
+
e.preventDefault();
|
|
672
|
+
const new_values = values.map((r) => [...r]);
|
|
673
|
+
selected_cells.forEach(([r, c]) => {
|
|
674
|
+
if (!static_columns.includes(c)) {
|
|
675
|
+
new_values[r][c] = "";
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
values = new_values;
|
|
679
|
+
push_change(new_values);
|
|
680
|
+
}
|
|
681
|
+
break;
|
|
682
|
+
default:
|
|
683
|
+
// start editing on printable character
|
|
684
|
+
if (
|
|
685
|
+
editable &&
|
|
686
|
+
!editing &&
|
|
687
|
+
e.key.length === 1 &&
|
|
688
|
+
!e.ctrlKey &&
|
|
689
|
+
!e.metaKey &&
|
|
690
|
+
!static_columns.includes(col)
|
|
691
|
+
) {
|
|
692
|
+
editing = [row, col];
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
523
695
|
}
|
|
524
|
-
}
|
|
525
696
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const calculated_width = `${Math.max(width, 45)}px`;
|
|
529
|
-
parent.style.setProperty(`--cell-width-${i}`, calculated_width);
|
|
697
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
698
|
+
handle_copy();
|
|
530
699
|
}
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function get_cell_width(index: number): string {
|
|
535
|
-
return `var(--cell-width-${index})`;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
let table_height: number =
|
|
539
|
-
values.slice(0, (max_height / values.length) * 37).length * 37 + 37;
|
|
540
|
-
let scrollbar_width = 0;
|
|
541
|
-
|
|
542
|
-
function sort_data(
|
|
543
|
-
_data: typeof data,
|
|
544
|
-
_display_value: string[][] | null,
|
|
545
|
-
_styling: string[][] | null
|
|
546
|
-
): void {
|
|
547
|
-
const result = sort_data_and_preserve_selection(
|
|
548
|
-
_data,
|
|
549
|
-
_display_value,
|
|
550
|
-
_styling,
|
|
551
|
-
$df_state.sort_state.sort_columns,
|
|
552
|
-
selected,
|
|
553
|
-
get_current_indices
|
|
554
|
-
);
|
|
555
|
-
|
|
556
|
-
data = result.data;
|
|
557
|
-
selected = result.selected;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
function filter_data(
|
|
561
|
-
_data: typeof data,
|
|
562
|
-
_display_value: string[][] | null,
|
|
563
|
-
_styling: string[][] | null
|
|
564
|
-
): void {
|
|
565
|
-
const result = filter_data_and_preserve_selection(
|
|
566
|
-
_data,
|
|
567
|
-
_display_value,
|
|
568
|
-
_styling,
|
|
569
|
-
$df_state.filter_state.filter_columns,
|
|
570
|
-
selected,
|
|
571
|
-
get_current_indices,
|
|
572
|
-
$df_state.filter_state.initial_data?.data,
|
|
573
|
-
$df_state.filter_state.initial_data?.display_value,
|
|
574
|
-
$df_state.filter_state.initial_data?.styling
|
|
575
|
-
);
|
|
576
|
-
data = result.data;
|
|
577
|
-
selected = result.selected;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
$: selected_index = !!selected && selected[0];
|
|
581
|
-
|
|
582
|
-
let is_visible = false;
|
|
583
|
-
|
|
584
|
-
const set_copy_flash = (value: boolean): void => {
|
|
585
|
-
df_actions.set_copy_flash(value);
|
|
586
|
-
if (value) {
|
|
587
|
-
setTimeout(() => df_actions.set_copy_flash(false), 800);
|
|
588
700
|
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
let previous_selected_cells: [number, number][] = [];
|
|
701
|
+
}
|
|
592
702
|
|
|
593
|
-
|
|
594
|
-
if (
|
|
595
|
-
|
|
703
|
+
function handle_scroll(): void {
|
|
704
|
+
if (scroll_container) {
|
|
705
|
+
show_scroll_button = scroll_container.scrollTop > 100;
|
|
596
706
|
}
|
|
597
|
-
previous_selected_cells = selected_cells;
|
|
598
707
|
}
|
|
599
708
|
|
|
600
|
-
function
|
|
601
|
-
|
|
602
|
-
blur_event: FocusEvent;
|
|
603
|
-
coords: [number, number];
|
|
604
|
-
}>
|
|
605
|
-
): void {
|
|
606
|
-
const { blur_event, coords } = event.detail;
|
|
607
|
-
handle_cell_blur(blur_event, df_ctx, coords);
|
|
709
|
+
function scroll_to_top(): void {
|
|
710
|
+
scroll_container?.scrollTo({ top: 0 });
|
|
608
711
|
}
|
|
609
712
|
|
|
610
|
-
function
|
|
713
|
+
function toggle_cell_menu(event: MouseEvent, row: number, col: number): void {
|
|
611
714
|
event.stopPropagation();
|
|
612
|
-
if (
|
|
613
|
-
|
|
715
|
+
if (active_cell_menu?.row === row && active_cell_menu.col === col) {
|
|
716
|
+
active_cell_menu = null;
|
|
614
717
|
} else {
|
|
615
|
-
const
|
|
616
|
-
if (
|
|
617
|
-
const rect =
|
|
618
|
-
|
|
619
|
-
col,
|
|
620
|
-
x: rect.right,
|
|
621
|
-
y: rect.bottom
|
|
622
|
-
});
|
|
718
|
+
const cell = (event.target as HTMLElement).closest(".body-cell, td");
|
|
719
|
+
if (cell) {
|
|
720
|
+
const rect = cell.getBoundingClientRect();
|
|
721
|
+
active_cell_menu = { row, col, x: rect.right, y: rect.bottom };
|
|
623
722
|
}
|
|
624
723
|
}
|
|
625
724
|
}
|
|
626
725
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
df_actions.set_active_header_menu(null);
|
|
641
|
-
df_actions.set_selected(false);
|
|
642
|
-
df_actions.set_selected_cells([]);
|
|
643
|
-
df_actions.set_editing(false);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
function delete_row_at(index: number): void {
|
|
647
|
-
data = df_actions.delete_row_at(data, index);
|
|
648
|
-
df_actions.set_active_cell_menu(null);
|
|
649
|
-
df_actions.set_active_header_menu(null);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
let selected_cell_coords: CellCoordinate;
|
|
653
|
-
$: if (selected !== false) selected_cell_coords = selected;
|
|
654
|
-
|
|
655
|
-
$: if (selected !== false) {
|
|
656
|
-
const positions = calculate_selection_positions(
|
|
657
|
-
selected,
|
|
658
|
-
data,
|
|
659
|
-
els,
|
|
660
|
-
parent,
|
|
661
|
-
table
|
|
662
|
-
);
|
|
663
|
-
document.documentElement.style.setProperty(
|
|
664
|
-
"--selected-col-pos",
|
|
665
|
-
positions.col_pos
|
|
666
|
-
);
|
|
667
|
-
document.documentElement.style.setProperty(
|
|
668
|
-
"--selected-row-pos",
|
|
669
|
-
positions.row_pos || "0px"
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
function commit_filter(): void {
|
|
674
|
-
if ($df_state.current_search_query && show_search === "filter") {
|
|
675
|
-
const filtered_data: CellValue[][] = [];
|
|
676
|
-
const filtered_display_values: string[][] = [];
|
|
677
|
-
const filtered_styling: string[][] = [];
|
|
678
|
-
|
|
679
|
-
search_results.forEach((row) => {
|
|
680
|
-
const data_row: CellValue[] = [];
|
|
681
|
-
const display_row: string[] = [];
|
|
682
|
-
const styling_row: string[] = [];
|
|
683
|
-
|
|
684
|
-
row.forEach((cell) => {
|
|
685
|
-
data_row.push(cell.value);
|
|
686
|
-
display_row.push(
|
|
687
|
-
cell.display_value !== undefined
|
|
688
|
-
? cell.display_value
|
|
689
|
-
: String(cell.value)
|
|
690
|
-
);
|
|
691
|
-
styling_row.push(cell.styling || "");
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
filtered_data.push(data_row);
|
|
695
|
-
filtered_display_values.push(display_row);
|
|
696
|
-
filtered_styling.push(styling_row);
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
const change_payload = {
|
|
700
|
-
data: filtered_data,
|
|
701
|
-
headers: _headers.map((h) => h.value),
|
|
702
|
-
metadata: {
|
|
703
|
-
display_value: filtered_display_values,
|
|
704
|
-
styling: filtered_styling
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
dispatch("change", change_payload);
|
|
709
|
-
|
|
710
|
-
if (!value_is_output) {
|
|
711
|
-
dispatch("input");
|
|
726
|
+
function on_file_upload(file_data: any): void {
|
|
727
|
+
handle_file_upload(
|
|
728
|
+
typeof file_data === "string" ? file_data : (file_data?.data ?? ""),
|
|
729
|
+
(head) => {
|
|
730
|
+
headers = head.map((h: any) => h ?? "");
|
|
731
|
+
return (headers as string[]).map((h: string, i: number) => ({
|
|
732
|
+
id: `h_${i}`,
|
|
733
|
+
value: h
|
|
734
|
+
}));
|
|
735
|
+
},
|
|
736
|
+
(vals) => {
|
|
737
|
+
values = vals;
|
|
738
|
+
push_change(vals, headers as string[]);
|
|
712
739
|
}
|
|
713
|
-
|
|
714
|
-
df_actions.handle_search(null);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
let viewport: HTMLTableElement;
|
|
719
|
-
let show_scroll_button = false;
|
|
720
|
-
|
|
721
|
-
function scroll_to_top(): void {
|
|
722
|
-
viewport.scrollTo({
|
|
723
|
-
top: 0
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
function handle_resize(): void {
|
|
728
|
-
df_actions.set_active_cell_menu(null);
|
|
729
|
-
df_actions.set_active_header_menu(null);
|
|
730
|
-
selected_cells = [];
|
|
731
|
-
selected = false;
|
|
732
|
-
editing = false;
|
|
733
|
-
width_calculated = false;
|
|
734
|
-
set_cell_widths();
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function add_row_at(index: number, position: "above" | "below"): void {
|
|
738
|
-
const row_index = position === "above" ? index : index + 1;
|
|
739
|
-
add_row(row_index);
|
|
740
|
-
active_cell_menu = null;
|
|
741
|
-
active_header_menu = null;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function add_col_at(index: number, position: "left" | "right"): void {
|
|
745
|
-
const col_index = position === "left" ? index : index + 1;
|
|
746
|
-
add_col(col_index);
|
|
747
|
-
active_cell_menu = null;
|
|
748
|
-
active_header_menu = null;
|
|
740
|
+
);
|
|
749
741
|
}
|
|
750
742
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
743
|
+
onMount(() => {
|
|
744
|
+
document.addEventListener("click", handle_click_outside);
|
|
745
|
+
return () => document.removeEventListener("click", handle_click_outside);
|
|
746
|
+
});
|
|
754
747
|
|
|
755
|
-
function
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
new_row[col] = {
|
|
760
|
-
...new_row[col],
|
|
761
|
-
value: checked
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
return new_row;
|
|
748
|
+
function measure_row(node: HTMLElement) {
|
|
749
|
+
tick().then(() => {
|
|
750
|
+
console.log("measuring");
|
|
751
|
+
virtualizer.instance.measureElement(node as any);
|
|
765
752
|
});
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
let is_dragging = false;
|
|
769
|
-
let drag_start: [number, number] | null = null;
|
|
770
|
-
let mouse_down_pos: { x: number; y: number } | null = null;
|
|
771
|
-
|
|
772
|
-
const drag_state: DragState = {
|
|
773
|
-
is_dragging,
|
|
774
|
-
drag_start,
|
|
775
|
-
mouse_down_pos
|
|
776
|
-
};
|
|
777
753
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
mouse_down_pos = drag_state.mouse_down_pos;
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
let drag_handlers: DragHandlers;
|
|
785
|
-
|
|
786
|
-
function init_drag_handlers(): void {
|
|
787
|
-
drag_handlers = create_drag_handlers(
|
|
788
|
-
drag_state,
|
|
789
|
-
(value) => (is_dragging = value),
|
|
790
|
-
(cells) => df_actions.set_selected_cells(cells),
|
|
791
|
-
(cell) => df_actions.set_selected(cell),
|
|
792
|
-
(event, row, col) => df_actions.handle_cell_click(event, row, col),
|
|
793
|
-
show_row_numbers,
|
|
794
|
-
parent
|
|
795
|
-
);
|
|
754
|
+
return {
|
|
755
|
+
destroy() {}
|
|
756
|
+
};
|
|
796
757
|
}
|
|
797
758
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
759
|
+
let header_row_el: HTMLTableRowElement;
|
|
760
|
+
let header_table_el: HTMLTableElement;
|
|
761
|
+
|
|
762
|
+
const measurement = create_column_measurement({
|
|
763
|
+
header_row_el: () => header_row_el,
|
|
764
|
+
header_table_el: () => header_table_el,
|
|
765
|
+
resolved_headers: () => resolved_headers,
|
|
766
|
+
row_data: () => row_data,
|
|
767
|
+
show_row_numbers: () => show_row_numbers,
|
|
768
|
+
column_widths: () => column_widths,
|
|
769
|
+
on_resize: undefined
|
|
770
|
+
});
|
|
803
771
|
|
|
804
|
-
|
|
805
|
-
|
|
772
|
+
let disable_scroll = $derived(
|
|
773
|
+
active_cell_menu !== null || active_header_menu !== null
|
|
774
|
+
);
|
|
775
|
+
let selected_index = $derived(selected !== false ? selected[0] : false);
|
|
806
776
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
: String(search_results[row][col].value);
|
|
777
|
+
$effect(() => {
|
|
778
|
+
if (typeof selected_index === "number") {
|
|
779
|
+
virtualizer.instance.scrollToIndex(selected_index, { align: "auto" });
|
|
811
780
|
}
|
|
781
|
+
});
|
|
812
782
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
783
|
+
function get_sort_info(col: number): {
|
|
784
|
+
direction: SortDirection | null;
|
|
785
|
+
priority: number | null;
|
|
786
|
+
} {
|
|
787
|
+
const col_id = `col_${col}`;
|
|
788
|
+
const idx = sorting.findIndex((s) => s.id === col_id);
|
|
789
|
+
if (idx === -1) return { direction: null, priority: null };
|
|
790
|
+
return { direction: sorting[idx].desc ? "desc" : "asc", priority: idx + 1 };
|
|
791
|
+
}
|
|
818
792
|
|
|
819
|
-
|
|
793
|
+
function get_filter_active(col: number): boolean {
|
|
794
|
+
return column_filters.some((f) => f.id === `col_${col}`);
|
|
820
795
|
}
|
|
821
796
|
</script>
|
|
822
797
|
|
|
823
|
-
<svelte:window
|
|
798
|
+
<svelte:window
|
|
799
|
+
onresize={() => {
|
|
800
|
+
active_cell_menu = null;
|
|
801
|
+
active_header_menu = null;
|
|
802
|
+
}}
|
|
803
|
+
/>
|
|
824
804
|
|
|
825
|
-
<div class="table-container">
|
|
805
|
+
<div class="table-container" class:fullscreen>
|
|
826
806
|
{#if (label && label.length !== 0 && show_label) || (buttons === null ? true : buttons.includes("fullscreen")) || (buttons === null ? true : buttons.includes("copy")) || show_search !== "none"}
|
|
827
807
|
<div class="header-row">
|
|
828
808
|
{#if label && label.length !== 0 && show_label}
|
|
829
|
-
<div class="label">
|
|
830
|
-
<p>{label}</p>
|
|
831
|
-
</div>
|
|
809
|
+
<div class="label"><p>{label}</p></div>
|
|
832
810
|
{/if}
|
|
833
811
|
<Toolbar
|
|
834
812
|
show_fullscreen_button={buttons === null
|
|
835
813
|
? true
|
|
836
814
|
: buttons.includes("fullscreen")}
|
|
837
815
|
{fullscreen}
|
|
838
|
-
on_copy={
|
|
816
|
+
on_copy={handle_copy}
|
|
839
817
|
show_copy_button={buttons === null ? true : buttons.includes("copy")}
|
|
840
818
|
{show_search}
|
|
841
|
-
|
|
842
|
-
|
|
819
|
+
onsearch={(query) => handle_search(query)}
|
|
820
|
+
{onfullscreen}
|
|
843
821
|
on_commit_filter={commit_filter}
|
|
844
|
-
current_search_query={
|
|
822
|
+
current_search_query={global_filter || null}
|
|
845
823
|
/>
|
|
846
824
|
</div>
|
|
847
825
|
{/if}
|
|
826
|
+
|
|
848
827
|
<div
|
|
849
828
|
bind:this={parent}
|
|
850
829
|
class="table-wrap"
|
|
851
830
|
class:dragging={is_dragging}
|
|
852
831
|
class:no-wrap={!wrap}
|
|
853
832
|
class:menu-open={active_cell_menu || active_header_menu}
|
|
854
|
-
|
|
855
|
-
on:mousemove={handle_mouse_move}
|
|
856
|
-
on:mouseup={handle_mouse_up}
|
|
857
|
-
on:mouseleave={handle_mouse_up}
|
|
833
|
+
onkeydown={handle_keydown}
|
|
858
834
|
role="grid"
|
|
859
835
|
tabindex="0"
|
|
860
836
|
>
|
|
861
|
-
<table bind:this={table} aria-hidden="true">
|
|
862
|
-
{#if label && label.length !== 0}
|
|
863
|
-
<caption class="sr-only">{label}</caption>
|
|
864
|
-
{/if}
|
|
865
|
-
<thead>
|
|
866
|
-
<tr>
|
|
867
|
-
{#if show_row_numbers}
|
|
868
|
-
<RowNumber is_header={true} />
|
|
869
|
-
{/if}
|
|
870
|
-
{#each _headers as { value, id }, i (id)}
|
|
871
|
-
<TableHeader
|
|
872
|
-
bind:value={_headers[i].value}
|
|
873
|
-
{i}
|
|
874
|
-
{actual_pinned_columns}
|
|
875
|
-
{header_edit}
|
|
876
|
-
{selected_header}
|
|
877
|
-
{headers}
|
|
878
|
-
{get_cell_width}
|
|
879
|
-
{handle_header_click}
|
|
880
|
-
{toggle_header_menu}
|
|
881
|
-
{end_header_edit}
|
|
882
|
-
sort_columns={$df_state.sort_state.sort_columns}
|
|
883
|
-
filter_columns={$df_state.filter_state.filter_columns}
|
|
884
|
-
{latex_delimiters}
|
|
885
|
-
{line_breaks}
|
|
886
|
-
{max_chars}
|
|
887
|
-
{editable}
|
|
888
|
-
is_static={static_columns.includes(i)}
|
|
889
|
-
{i18n}
|
|
890
|
-
bind:el={els[id].input}
|
|
891
|
-
{col_count}
|
|
892
|
-
datatype={Array.isArray(datatype) ? datatype[i] : datatype}
|
|
893
|
-
{data}
|
|
894
|
-
on_select_all={handle_select_all}
|
|
895
|
-
/>
|
|
896
|
-
{/each}
|
|
897
|
-
</tr>
|
|
898
|
-
</thead>
|
|
899
|
-
<tbody>
|
|
900
|
-
<tr>
|
|
901
|
-
{#if show_row_numbers}
|
|
902
|
-
<RowNumber index={0} />
|
|
903
|
-
{/if}
|
|
904
|
-
{#each max as { value, id }, j (id)}
|
|
905
|
-
<td tabindex="-1" bind:this={cells[j]}>
|
|
906
|
-
<div class="cell-wrap">
|
|
907
|
-
<EditableCell
|
|
908
|
-
{value}
|
|
909
|
-
{latex_delimiters}
|
|
910
|
-
{line_breaks}
|
|
911
|
-
datatype={Array.isArray(datatype) ? datatype[j] : datatype}
|
|
912
|
-
edit={false}
|
|
913
|
-
el={null}
|
|
914
|
-
{editable}
|
|
915
|
-
{i18n}
|
|
916
|
-
show_selection_buttons={selected_cells.length === 1 &&
|
|
917
|
-
selected_cells[0][0] === 0 &&
|
|
918
|
-
selected_cells[0][1] === j}
|
|
919
|
-
coords={selected_cell_coords}
|
|
920
|
-
on_select_column={df_actions.handle_select_column}
|
|
921
|
-
on_select_row={df_actions.handle_select_row}
|
|
922
|
-
{is_dragging}
|
|
923
|
-
on:blur={handle_blur}
|
|
924
|
-
/>
|
|
925
|
-
</div>
|
|
926
|
-
</td>
|
|
927
|
-
{/each}
|
|
928
|
-
</tr>
|
|
929
|
-
</tbody>
|
|
930
|
-
</table>
|
|
931
837
|
<Upload
|
|
932
838
|
{upload}
|
|
933
839
|
{stream_handler}
|
|
@@ -936,125 +842,197 @@
|
|
|
936
842
|
boundedheight={false}
|
|
937
843
|
disable_click={true}
|
|
938
844
|
{root}
|
|
939
|
-
|
|
940
|
-
handle_file_upload(
|
|
941
|
-
detail.data,
|
|
942
|
-
(head) => {
|
|
943
|
-
_headers = make_headers(
|
|
944
|
-
head.map((h) => h ?? ""),
|
|
945
|
-
col_count,
|
|
946
|
-
els,
|
|
947
|
-
make_id
|
|
948
|
-
);
|
|
949
|
-
return _headers;
|
|
950
|
-
},
|
|
951
|
-
(vals) => {
|
|
952
|
-
values = vals;
|
|
953
|
-
}
|
|
954
|
-
)}
|
|
845
|
+
onload={on_file_upload}
|
|
955
846
|
bind:dragging
|
|
956
847
|
aria_label={i18n("dataframe.drop_to_upload")}
|
|
957
848
|
>
|
|
958
|
-
<div
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
849
|
+
<div
|
|
850
|
+
class="virtual-table-viewport"
|
|
851
|
+
class:disable-scroll={disable_scroll}
|
|
852
|
+
bind:this={scroll_container}
|
|
853
|
+
onscroll={handle_scroll}
|
|
854
|
+
style="max-height: {max_height}px;"
|
|
855
|
+
role="grid"
|
|
856
|
+
>
|
|
857
|
+
{#if label && label.length !== 0}
|
|
858
|
+
<span class="sr-only">{label}</span>
|
|
859
|
+
{/if}
|
|
860
|
+
<!-- header row: uses table layout to auto-size columns by content -->
|
|
861
|
+
<table class="header-table" bind:this={header_table_el}>
|
|
862
|
+
<thead>
|
|
863
|
+
<tr bind:this={header_row_el}>
|
|
864
|
+
{#if show_row_numbers}
|
|
865
|
+
<th class="row-number-header"> </th>
|
|
866
|
+
{/if}
|
|
867
|
+
{#each header_groups as headerGroup (headerGroup.id)}
|
|
868
|
+
{#each headerGroup.headers as header (header.id)}
|
|
869
|
+
{@const col_idx =
|
|
870
|
+
(header.column.columnDef.meta as any)?.colIndex ?? 0}
|
|
871
|
+
<HeaderCell
|
|
872
|
+
value={String(header.column.columnDef.header ?? "")}
|
|
873
|
+
{col_idx}
|
|
874
|
+
is_editing={header_edit === col_idx}
|
|
875
|
+
is_selected={selected_header === col_idx}
|
|
876
|
+
is_static={!!(header.column.columnDef.meta as any)
|
|
877
|
+
?.isStatic}
|
|
878
|
+
sort_direction={get_sort_info(col_idx).direction}
|
|
879
|
+
sort_priority={get_sort_info(col_idx).priority}
|
|
880
|
+
multi_sort={sorting.length > 1}
|
|
881
|
+
is_filtered={get_filter_active(col_idx)}
|
|
882
|
+
show_menu_button={col_count[1] === "dynamic"}
|
|
883
|
+
is_first_column={col_idx === 0 && !show_row_numbers}
|
|
884
|
+
{latex_delimiters}
|
|
885
|
+
{line_breaks}
|
|
886
|
+
{editable}
|
|
887
|
+
{max_chars}
|
|
888
|
+
{i18n}
|
|
889
|
+
onclick={handle_header_click}
|
|
890
|
+
on_menu_click={toggle_header_menu}
|
|
891
|
+
on_end_edit={end_header_edit}
|
|
892
|
+
/>
|
|
893
|
+
{/each}
|
|
894
|
+
{/each}
|
|
895
|
+
</tr>
|
|
896
|
+
</thead>
|
|
897
|
+
<!-- hidden sizing row: lets table-layout:auto consider body content widths too -->
|
|
898
|
+
<tbody class="sizing-body" aria-hidden="true">
|
|
899
|
+
{#if rows.length > 0}
|
|
900
|
+
{@const sizing_row = rows.reduce((widest, row) => {
|
|
901
|
+
const cells = row.getVisibleCells();
|
|
902
|
+
cells.forEach((cell, i) => {
|
|
903
|
+
const val = String(cell.getValue() ?? "");
|
|
904
|
+
if (!widest[i] || val.length > widest[i].length) {
|
|
905
|
+
widest[i] = val;
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
return widest;
|
|
909
|
+
}, [] as string[])}
|
|
910
|
+
<tr>
|
|
911
|
+
{#if show_row_numbers}
|
|
912
|
+
<td class="row-number-cell">{rows.length}</td>
|
|
913
|
+
{/if}
|
|
914
|
+
{#each sizing_row as val, ci}
|
|
915
|
+
{@const dtype = get_dtype(ci)}
|
|
916
|
+
<td
|
|
917
|
+
><div class="cell-wrap">
|
|
918
|
+
{#if dtype === "html" || dtype === "markdown"}{@html val}{:else}{val}{/if}
|
|
919
|
+
</div></td
|
|
920
|
+
>
|
|
921
|
+
{/each}
|
|
922
|
+
</tr>
|
|
975
923
|
{/if}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
{
|
|
993
|
-
{
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
{
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
924
|
+
</tbody>
|
|
925
|
+
</table>
|
|
926
|
+
|
|
927
|
+
<!-- table body: absolutely positioned rows (standard tanstack virtual pattern) -->
|
|
928
|
+
<div
|
|
929
|
+
class="virtual-body"
|
|
930
|
+
style="height: {total_size}px; position: relative; flex-shrink: 0; width: {measurement.total_header_width
|
|
931
|
+
? `${measurement.total_header_width}px`
|
|
932
|
+
: '100%'};"
|
|
933
|
+
>
|
|
934
|
+
{#each virtual_items as virtual_row (virtual_row.key)}
|
|
935
|
+
{@const row = rows[virtual_row.index]}
|
|
936
|
+
{@const row_idx = row?.original._index ?? virtual_row.index}
|
|
937
|
+
{#if row}
|
|
938
|
+
<div
|
|
939
|
+
class="virtual-row"
|
|
940
|
+
class:row-odd={virtual_row.index % 2 !== 0}
|
|
941
|
+
data-index={virtual_row.index}
|
|
942
|
+
style="position: absolute; top: 0; left: 0; width: 100%; transform: translateY({virtual_row.start}px);{selected_cells.some(
|
|
943
|
+
([r]) => r === row_idx
|
|
944
|
+
)
|
|
945
|
+
? ' z-index: 3;'
|
|
946
|
+
: ''}"
|
|
947
|
+
use:measure_row={row}
|
|
948
|
+
>
|
|
949
|
+
{#if show_row_numbers}
|
|
950
|
+
<div
|
|
951
|
+
class="row-number-cell"
|
|
952
|
+
data-row={row_idx}
|
|
953
|
+
data-col="row-number"
|
|
954
|
+
style="flex: 0 0 {measurement.row_num_width}px; width: {measurement.row_num_width}px;"
|
|
955
|
+
>
|
|
956
|
+
{row_idx + 1}
|
|
957
|
+
</div>
|
|
958
|
+
{/if}
|
|
959
|
+
{#each row.getVisibleCells() as cell, ci (cell.id)}
|
|
960
|
+
{@const col_idx =
|
|
961
|
+
(cell.column.columnDef.meta as any)?.colIndex ?? 0}
|
|
962
|
+
{@const is_sel = is_cell_in_selection(
|
|
963
|
+
[row_idx, col_idx],
|
|
964
|
+
selected_cells
|
|
965
|
+
)}
|
|
966
|
+
<DataCell
|
|
967
|
+
value={cell.getValue() as CellValue}
|
|
968
|
+
display_value={get_display_value(row_idx, col_idx)}
|
|
969
|
+
datatype={get_dtype(col_idx)}
|
|
970
|
+
{row_idx}
|
|
971
|
+
{col_idx}
|
|
972
|
+
col_style={measurement.get_col_style(ci)}
|
|
973
|
+
cell_style={get_styling(row_idx, col_idx)}
|
|
974
|
+
selection_classes={is_cell_selected(
|
|
975
|
+
[row_idx, col_idx],
|
|
976
|
+
selected_cells
|
|
977
|
+
)}
|
|
978
|
+
is_editing={!!(
|
|
979
|
+
editing &&
|
|
980
|
+
editing[0] === row_idx &&
|
|
981
|
+
editing[1] === col_idx
|
|
982
|
+
)}
|
|
983
|
+
is_flash={copy_flash && is_sel}
|
|
984
|
+
is_static={!!(cell.column.columnDef.meta as any)?.isStatic}
|
|
985
|
+
show_menu_button={editable &&
|
|
986
|
+
selected_cells.length === 1 &&
|
|
987
|
+
selected_cells[0][0] === row_idx &&
|
|
988
|
+
selected_cells[0][1] === col_idx}
|
|
989
|
+
show_selection_buttons={selected_cells.length === 1 &&
|
|
990
|
+
selected_cells[0][0] === row_idx &&
|
|
991
|
+
selected_cells[0][1] === col_idx}
|
|
992
|
+
is_first_column={ci === 0 && !show_row_numbers}
|
|
993
|
+
{latex_delimiters}
|
|
994
|
+
{line_breaks}
|
|
995
|
+
{editable}
|
|
996
|
+
{max_chars}
|
|
997
|
+
{i18n}
|
|
998
|
+
{components}
|
|
999
|
+
{is_dragging}
|
|
1000
|
+
wrap_text={wrap}
|
|
1001
|
+
onmousedown={(e) => handle_cell_click(e, row_idx, col_idx)}
|
|
1002
|
+
ondblclick={(e) =>
|
|
1003
|
+
handle_cell_dblclick(e, row_idx, col_idx)}
|
|
1004
|
+
oncontextmenu={(e) => {
|
|
1005
|
+
e.preventDefault();
|
|
1006
|
+
toggle_cell_menu(e, row_idx, col_idx);
|
|
1007
|
+
}}
|
|
1008
|
+
onblur={handle_blur}
|
|
1009
|
+
on_menu_click={(e) => toggle_cell_menu(e, row_idx, col_idx)}
|
|
1010
|
+
on_select_column={(c) => {
|
|
1011
|
+
selected_cells = rows.map(
|
|
1012
|
+
(_, r) => [r, c] as CellCoordinate
|
|
1013
|
+
);
|
|
1014
|
+
selected = selected_cells[0];
|
|
1015
|
+
}}
|
|
1016
|
+
on_select_row={(r) => {
|
|
1017
|
+
selected_cells = resolved_headers.map(
|
|
1018
|
+
(_, c) => [r, c] as CellCoordinate
|
|
1019
|
+
);
|
|
1020
|
+
selected = selected_cells[0];
|
|
1021
|
+
}}
|
|
1022
|
+
/>
|
|
1023
|
+
{/each}
|
|
1024
|
+
</div>
|
|
1007
1025
|
{/if}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
bind:value={search_results[index][j].value}
|
|
1011
|
-
display_value={get_cell_display_value(index, j)}
|
|
1012
|
-
index={$df_state.current_search_query !== undefined &&
|
|
1013
|
-
filtered_to_original_map[index] !== undefined
|
|
1014
|
-
? filtered_to_original_map[index]
|
|
1015
|
-
: index}
|
|
1016
|
-
{j}
|
|
1017
|
-
{actual_pinned_columns}
|
|
1018
|
-
{get_cell_width}
|
|
1019
|
-
handle_cell_click={(e, r, c) => handle_mouse_down(e, r, c)}
|
|
1020
|
-
{handle_blur}
|
|
1021
|
-
toggle_cell_menu={df_actions.toggle_cell_menu}
|
|
1022
|
-
{is_cell_selected}
|
|
1023
|
-
{should_show_cell_menu}
|
|
1024
|
-
{selected_cells}
|
|
1025
|
-
{copy_flash}
|
|
1026
|
-
{active_cell_menu}
|
|
1027
|
-
styling={search_results[index][j].styling}
|
|
1028
|
-
{latex_delimiters}
|
|
1029
|
-
{line_breaks}
|
|
1030
|
-
datatype={Array.isArray(datatype) ? datatype[j] : datatype}
|
|
1031
|
-
{editing}
|
|
1032
|
-
{max_chars}
|
|
1033
|
-
{editable}
|
|
1034
|
-
is_static={static_columns.includes(j)}
|
|
1035
|
-
{i18n}
|
|
1036
|
-
{components}
|
|
1037
|
-
handle_select_column={df_actions.handle_select_column}
|
|
1038
|
-
handle_select_row={df_actions.handle_select_row}
|
|
1039
|
-
bind:el={els[id]}
|
|
1040
|
-
{is_dragging}
|
|
1041
|
-
{wrap}
|
|
1042
|
-
/>
|
|
1043
|
-
{/each}
|
|
1044
|
-
</tr>
|
|
1045
|
-
</VirtualTable>
|
|
1026
|
+
{/each}
|
|
1027
|
+
</div>
|
|
1046
1028
|
</div>
|
|
1047
1029
|
</Upload>
|
|
1030
|
+
|
|
1048
1031
|
{#if show_scroll_button}
|
|
1049
|
-
<button class="scroll-top-button"
|
|
1050
|
-
↑
|
|
1051
|
-
</button>
|
|
1032
|
+
<button class="scroll-top-button" onclick={scroll_to_top}>↑</button>
|
|
1052
1033
|
{/if}
|
|
1053
1034
|
</div>
|
|
1054
1035
|
</div>
|
|
1055
|
-
{#if data.length === 0 && editable && row_count[1] === "dynamic"}
|
|
1056
|
-
<EmptyRowButton on_click={() => add_row()} />
|
|
1057
|
-
{/if}
|
|
1058
1036
|
|
|
1059
1037
|
{#if active_cell_menu || active_header_menu}
|
|
1060
1038
|
<CellMenu
|
|
@@ -1079,66 +1057,96 @@
|
|
|
1079
1057
|
on_delete_col={() =>
|
|
1080
1058
|
delete_col_at(active_cell_menu?.col ?? active_header_menu?.col ?? -1)}
|
|
1081
1059
|
{editable}
|
|
1082
|
-
can_delete_rows={!active_header_menu &&
|
|
1083
|
-
can_delete_cols={
|
|
1060
|
+
can_delete_rows={!active_header_menu && values.length > 1 && editable}
|
|
1061
|
+
can_delete_cols={values.length > 0 &&
|
|
1062
|
+
(values[0]?.length ?? 0) > 1 &&
|
|
1063
|
+
editable}
|
|
1084
1064
|
{i18n}
|
|
1085
1065
|
on_sort={active_header_menu
|
|
1086
1066
|
? (direction) => {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
df_actions.set_active_header_menu(null);
|
|
1090
|
-
}
|
|
1067
|
+
handle_sort(active_header_menu!.col, direction);
|
|
1068
|
+
active_header_menu = null;
|
|
1091
1069
|
}
|
|
1092
1070
|
: undefined}
|
|
1093
1071
|
on_clear_sort={active_header_menu
|
|
1094
1072
|
? () => {
|
|
1095
1073
|
clear_sort();
|
|
1096
|
-
|
|
1074
|
+
active_header_menu = null;
|
|
1097
1075
|
}
|
|
1098
1076
|
: undefined}
|
|
1099
1077
|
sort_direction={active_header_menu
|
|
1100
|
-
? (
|
|
1101
|
-
(item) => item.col === (active_header_menu?.col ?? -1)
|
|
1102
|
-
)?.direction ?? null)
|
|
1078
|
+
? get_sort_info(active_header_menu.col).direction
|
|
1103
1079
|
: null}
|
|
1104
1080
|
sort_priority={active_header_menu
|
|
1105
|
-
?
|
|
1106
|
-
(item) => item.col === (active_header_menu?.col ?? -1)
|
|
1107
|
-
) + 1 || null
|
|
1081
|
+
? get_sort_info(active_header_menu.col).priority
|
|
1108
1082
|
: null}
|
|
1109
1083
|
on_filter={active_header_menu
|
|
1110
|
-
? (
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
df_actions.set_active_header_menu(null);
|
|
1114
|
-
}
|
|
1084
|
+
? (dtype, filter, fvalue) => {
|
|
1085
|
+
handle_filter(active_header_menu!.col, dtype, filter, fvalue);
|
|
1086
|
+
active_header_menu = null;
|
|
1115
1087
|
}
|
|
1116
1088
|
: undefined}
|
|
1117
1089
|
on_clear_filter={active_header_menu
|
|
1118
1090
|
? () => {
|
|
1119
1091
|
clear_filter();
|
|
1120
|
-
|
|
1092
|
+
active_header_menu = null;
|
|
1121
1093
|
}
|
|
1122
1094
|
: undefined}
|
|
1123
1095
|
filter_active={active_header_menu
|
|
1124
|
-
?
|
|
1125
|
-
(c) => c.col === (active_header_menu?.col ?? -1)
|
|
1126
|
-
)
|
|
1096
|
+
? get_filter_active(active_header_menu.col)
|
|
1127
1097
|
: null}
|
|
1128
1098
|
/>
|
|
1129
1099
|
{/if}
|
|
1130
1100
|
|
|
1101
|
+
{#if values.length === 0 && editable && row_count[1] === "dynamic"}
|
|
1102
|
+
<EmptyRowButton on_click={() => add_row()} />
|
|
1103
|
+
{/if}
|
|
1104
|
+
|
|
1131
1105
|
<style>
|
|
1132
1106
|
.table-container {
|
|
1133
1107
|
display: flex;
|
|
1134
1108
|
flex-direction: column;
|
|
1135
1109
|
gap: var(--size-2);
|
|
1136
1110
|
position: relative;
|
|
1111
|
+
max-width: 100%;
|
|
1112
|
+
overflow: hidden;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
.table-container.fullscreen {
|
|
1116
|
+
padding: var(--size-4);
|
|
1117
|
+
height: 100%;
|
|
1118
|
+
box-sizing: border-box;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
.table-container.fullscreen .table-wrap {
|
|
1122
|
+
flex: 1 1 auto;
|
|
1123
|
+
min-height: 0;
|
|
1124
|
+
display: flex;
|
|
1125
|
+
flex-direction: column;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
.table-container.fullscreen .table-wrap > :global(*) {
|
|
1129
|
+
flex: 1 1 auto;
|
|
1130
|
+
min-height: 0;
|
|
1131
|
+
display: flex;
|
|
1132
|
+
flex-direction: column;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
.table-container.fullscreen .virtual-table-viewport {
|
|
1136
|
+
max-height: none !important;
|
|
1137
|
+
flex: 1 1 auto;
|
|
1138
|
+
min-height: 0;
|
|
1137
1139
|
}
|
|
1138
1140
|
|
|
1139
1141
|
.table-wrap {
|
|
1140
1142
|
position: relative;
|
|
1141
1143
|
transition: 150ms;
|
|
1144
|
+
width: 100%;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/* Constrain Upload component wrapper */
|
|
1148
|
+
.table-wrap > :global(*) {
|
|
1149
|
+
max-width: 100%;
|
|
1142
1150
|
}
|
|
1143
1151
|
|
|
1144
1152
|
.table-wrap.menu-open {
|
|
@@ -1165,56 +1173,147 @@
|
|
|
1165
1173
|
overflow: hidden;
|
|
1166
1174
|
}
|
|
1167
1175
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1176
|
+
/* Virtual scroll container */
|
|
1177
|
+
.virtual-table-viewport {
|
|
1178
|
+
display: flex;
|
|
1179
|
+
flex-direction: column;
|
|
1180
|
+
overflow: auto;
|
|
1181
|
+
position: relative;
|
|
1182
|
+
-webkit-overflow-scrolling: touch;
|
|
1183
|
+
min-width: 0;
|
|
1184
|
+
max-width: 100%;
|
|
1185
|
+
scrollbar-width: thin;
|
|
1186
|
+
scrollbar-color: rgba(128, 128, 128, 0.5) transparent;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
.virtual-table-viewport::-webkit-scrollbar {
|
|
1190
|
+
width: 4px;
|
|
1191
|
+
height: 4px;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
.virtual-table-viewport::-webkit-scrollbar-thumb {
|
|
1195
|
+
background-color: rgba(128, 128, 128, 0.5);
|
|
1196
|
+
border-radius: 4px;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
.virtual-table-viewport:hover {
|
|
1200
|
+
scrollbar-color: rgba(160, 160, 160, 0.7) transparent;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
.virtual-table-viewport.disable-scroll {
|
|
1204
|
+
overflow: hidden !important;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/* Header table: auto-sizes columns by content, sticky */
|
|
1208
|
+
.header-table {
|
|
1209
|
+
width: 100%;
|
|
1175
1210
|
color: var(--body-text-color);
|
|
1176
1211
|
font-size: var(--input-text-size);
|
|
1177
1212
|
line-height: var(--line-md);
|
|
1178
1213
|
font-family: var(--font-mono);
|
|
1179
1214
|
border-spacing: 0;
|
|
1180
1215
|
border-collapse: separate;
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
thead {
|
|
1216
|
+
table-layout: auto;
|
|
1184
1217
|
position: sticky;
|
|
1185
1218
|
top: 0;
|
|
1186
|
-
z-index:
|
|
1187
|
-
|
|
1219
|
+
z-index: 7;
|
|
1220
|
+
flex-shrink: 0;
|
|
1188
1221
|
}
|
|
1189
1222
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
background: var(--table-even-background-fill) !important;
|
|
1223
|
+
/* Hidden sizing row — visibility:collapse hides the row but keeps column width contribution */
|
|
1224
|
+
.sizing-body tr {
|
|
1225
|
+
visibility: collapse;
|
|
1194
1226
|
}
|
|
1195
1227
|
|
|
1196
|
-
.
|
|
1197
|
-
|
|
1228
|
+
.sizing-body td {
|
|
1229
|
+
padding: var(--size-2);
|
|
1230
|
+
border: none;
|
|
1231
|
+
white-space: nowrap;
|
|
1198
1232
|
}
|
|
1199
1233
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1234
|
+
/* Virtual body */
|
|
1235
|
+
.virtual-body {
|
|
1236
|
+
box-sizing: border-box;
|
|
1202
1237
|
}
|
|
1203
1238
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1239
|
+
.virtual-row {
|
|
1240
|
+
display: flex;
|
|
1241
|
+
align-items: stretch;
|
|
1242
|
+
background: var(--table-odd-background-fill);
|
|
1243
|
+
|
|
1244
|
+
text-align: left;
|
|
1245
|
+
font-size: var(--input-text-size);
|
|
1246
|
+
line-height: var(--line-md);
|
|
1247
|
+
font-family: var(--font-mono);
|
|
1248
|
+
color: var(--body-text-color);
|
|
1249
|
+
min-height: var(--size-9);
|
|
1206
1250
|
}
|
|
1207
1251
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1252
|
+
.virtual-row:last-child {
|
|
1253
|
+
border-bottom: none;
|
|
1210
1254
|
}
|
|
1211
1255
|
|
|
1212
|
-
|
|
1256
|
+
.virtual-row.row-odd {
|
|
1213
1257
|
background: var(--table-even-background-fill);
|
|
1214
1258
|
}
|
|
1215
1259
|
|
|
1216
|
-
|
|
1217
|
-
background: var(--table-
|
|
1260
|
+
.virtual-row:hover {
|
|
1261
|
+
background: var(--table-row-focus);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/* Cell content wrapper (used in sizing-body) */
|
|
1265
|
+
.cell-wrap {
|
|
1266
|
+
display: flex;
|
|
1267
|
+
align-items: center;
|
|
1268
|
+
justify-content: flex-start;
|
|
1269
|
+
outline: none;
|
|
1270
|
+
min-height: var(--size-9);
|
|
1271
|
+
position: relative;
|
|
1272
|
+
height: 100%;
|
|
1273
|
+
padding: var(--size-2);
|
|
1274
|
+
box-sizing: border-box;
|
|
1275
|
+
gap: var(--size-1);
|
|
1276
|
+
overflow: visible;
|
|
1277
|
+
min-width: 0;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
/* Row number cells */
|
|
1281
|
+
.row-number-header {
|
|
1282
|
+
text-align: center;
|
|
1283
|
+
padding: var(--size-1);
|
|
1284
|
+
min-width: var(--size-12);
|
|
1285
|
+
width: var(--size-12);
|
|
1286
|
+
font-weight: var(--weight-semibold);
|
|
1287
|
+
border-right: 1px solid var(--border-color-primary);
|
|
1288
|
+
background: var(--table-even-background-fill) !important;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.row-number-cell {
|
|
1292
|
+
text-align: center;
|
|
1293
|
+
padding: var(--size-1);
|
|
1294
|
+
min-width: var(--size-12);
|
|
1295
|
+
width: var(--size-12);
|
|
1296
|
+
flex-shrink: 0;
|
|
1297
|
+
overflow: hidden;
|
|
1298
|
+
text-overflow: ellipsis;
|
|
1299
|
+
white-space: nowrap;
|
|
1300
|
+
font-weight: var(--weight-semibold);
|
|
1301
|
+
border-right: 1px solid var(--border-color-primary);
|
|
1302
|
+
display: flex;
|
|
1303
|
+
align-items: center;
|
|
1304
|
+
justify-content: center;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.no-wrap {
|
|
1308
|
+
white-space: nowrap;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
div:not(.no-wrap) :global(td) {
|
|
1312
|
+
overflow-wrap: anywhere;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
div.no-wrap :global(td) {
|
|
1316
|
+
overflow-x: hidden;
|
|
1218
1317
|
}
|
|
1219
1318
|
|
|
1220
1319
|
.header-row {
|
|
@@ -1238,8 +1337,6 @@
|
|
|
1238
1337
|
color: var(--block-label-text-color);
|
|
1239
1338
|
font-size: var(--block-label-text-size);
|
|
1240
1339
|
line-height: var(--line-sm);
|
|
1241
|
-
position: relative;
|
|
1242
|
-
z-index: 4;
|
|
1243
1340
|
}
|
|
1244
1341
|
|
|
1245
1342
|
.scroll-top-button {
|
|
@@ -1264,9 +1361,4 @@
|
|
|
1264
1361
|
.scroll-top-button:hover {
|
|
1265
1362
|
opacity: 1;
|
|
1266
1363
|
}
|
|
1267
|
-
|
|
1268
|
-
tr {
|
|
1269
|
-
border-bottom: 1px solid var(--border-color-primary);
|
|
1270
|
-
text-align: left;
|
|
1271
|
-
}
|
|
1272
1364
|
</style>
|