@gradio/dataframe 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/Dataframe.stories.svelte +115 -5
- package/Index.svelte +2 -0
- package/dist/Index.svelte +2 -0
- package/dist/Index.svelte.d.ts +4 -0
- package/dist/shared/EditableCell.svelte +52 -7
- package/dist/shared/EditableCell.svelte.d.ts +1 -1
- package/dist/shared/Table.svelte +301 -262
- package/dist/shared/Table.svelte.d.ts +1 -0
- package/dist/shared/selection_utils.d.ts +20 -0
- package/dist/shared/selection_utils.js +111 -0
- package/dist/shared/table_utils.d.ts +7 -1
- package/dist/shared/table_utils.js +96 -10
- package/dist/shared/types.d.ts +2 -0
- package/dist/shared/types.js +1 -0
- package/package.json +5 -5
- package/shared/EditableCell.svelte +58 -7
- package/shared/Table.svelte +342 -329
- package/shared/selection_utils.ts +188 -0
- package/shared/table_utils.ts +120 -10
- package/shared/types.ts +2 -0
package/shared/Table.svelte
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { afterUpdate, createEventDispatcher, tick, onMount } from "svelte";
|
|
3
|
-
import { dsvFormat } from "d3-dsv";
|
|
4
3
|
import { dequal } from "dequal/lite";
|
|
5
4
|
import { Upload } from "@gradio/upload";
|
|
6
5
|
|
|
@@ -17,7 +16,19 @@
|
|
|
17
16
|
} from "./utils";
|
|
18
17
|
import CellMenu from "./CellMenu.svelte";
|
|
19
18
|
import Toolbar from "./Toolbar.svelte";
|
|
20
|
-
import {
|
|
19
|
+
import type { CellCoordinate, EditingState } from "./types";
|
|
20
|
+
import {
|
|
21
|
+
is_cell_selected,
|
|
22
|
+
handle_selection,
|
|
23
|
+
handle_delete_key,
|
|
24
|
+
should_show_cell_menu,
|
|
25
|
+
get_next_cell_coordinates,
|
|
26
|
+
get_range_selection,
|
|
27
|
+
move_cursor,
|
|
28
|
+
get_current_indices,
|
|
29
|
+
handle_click_outside as handle_click_outside_util
|
|
30
|
+
} from "./selection_utils";
|
|
31
|
+
import { copy_table_data, get_max, handle_file_upload } from "./table_utils";
|
|
21
32
|
|
|
22
33
|
export let datatype: Datatype | Datatype[];
|
|
23
34
|
export let label: string | null = null;
|
|
@@ -46,12 +57,24 @@
|
|
|
46
57
|
export let show_fullscreen_button = false;
|
|
47
58
|
export let show_copy_button = false;
|
|
48
59
|
export let value_is_output = false;
|
|
60
|
+
export let max_chars: number | undefined = undefined;
|
|
61
|
+
|
|
62
|
+
let selected_cells: CellCoordinate[] = [];
|
|
63
|
+
$: selected_cells = [...selected_cells];
|
|
64
|
+
let selected: CellCoordinate | false = false;
|
|
65
|
+
$: selected =
|
|
66
|
+
selected_cells.length > 0
|
|
67
|
+
? selected_cells[selected_cells.length - 1]
|
|
68
|
+
: false;
|
|
49
69
|
|
|
50
|
-
let selected: false | [number, number] = false;
|
|
51
|
-
let clicked_cell: { row: number; col: number } | undefined = undefined;
|
|
52
70
|
export let display_value: string[][] | null = null;
|
|
53
71
|
export let styling: string[][] | null = null;
|
|
54
72
|
let t_rect: DOMRectReadOnly;
|
|
73
|
+
let els: Record<
|
|
74
|
+
string,
|
|
75
|
+
{ cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
|
|
76
|
+
> = {};
|
|
77
|
+
let data_binding: Record<string, (typeof data)[0][0]> = {};
|
|
55
78
|
|
|
56
79
|
const dispatch = createEventDispatcher<{
|
|
57
80
|
change: DataframeValue;
|
|
@@ -59,34 +82,27 @@
|
|
|
59
82
|
select: SelectData;
|
|
60
83
|
}>();
|
|
61
84
|
|
|
62
|
-
let editing:
|
|
85
|
+
let editing: EditingState = false;
|
|
86
|
+
let clear_on_focus = false;
|
|
87
|
+
let header_edit: number | false = false;
|
|
88
|
+
let selected_header: number | false = false;
|
|
89
|
+
let active_cell_menu: {
|
|
90
|
+
row: number;
|
|
91
|
+
col: number;
|
|
92
|
+
x: number;
|
|
93
|
+
y: number;
|
|
94
|
+
} | null = null;
|
|
95
|
+
let active_header_menu: {
|
|
96
|
+
col: number;
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
} | null = null;
|
|
100
|
+
let is_fullscreen = false;
|
|
101
|
+
let dragging = false;
|
|
63
102
|
|
|
64
103
|
const get_data_at = (row: number, col: number): string | number =>
|
|
65
104
|
data?.[row]?.[col]?.value;
|
|
66
105
|
|
|
67
|
-
let last_selected: [number, number] | null = null;
|
|
68
|
-
|
|
69
|
-
$: {
|
|
70
|
-
if (selected !== false && !dequal(selected, last_selected)) {
|
|
71
|
-
const [row, col] = selected;
|
|
72
|
-
if (!isNaN(row) && !isNaN(col) && data[row]) {
|
|
73
|
-
dispatch("select", {
|
|
74
|
-
index: [row, col],
|
|
75
|
-
value: get_data_at(row, col),
|
|
76
|
-
row_value: data[row].map((d) => d.value)
|
|
77
|
-
});
|
|
78
|
-
last_selected = selected;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let els: Record<
|
|
84
|
-
string,
|
|
85
|
-
{ cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
|
|
86
|
-
> = {};
|
|
87
|
-
|
|
88
|
-
let data_binding: Record<string, (typeof data)[0][0]> = {};
|
|
89
|
-
|
|
90
106
|
function make_id(): string {
|
|
91
107
|
return Math.random().toString(36).substring(2, 15);
|
|
92
108
|
}
|
|
@@ -199,48 +215,9 @@
|
|
|
199
215
|
if (direction === "asc") return "ascending";
|
|
200
216
|
if (direction === "des") return "descending";
|
|
201
217
|
}
|
|
202
|
-
|
|
203
218
|
return "none";
|
|
204
219
|
}
|
|
205
220
|
|
|
206
|
-
function get_current_indices(id: string): [number, number] {
|
|
207
|
-
return data.reduce(
|
|
208
|
-
(acc, arr, i) => {
|
|
209
|
-
const j = arr.reduce(
|
|
210
|
-
(_acc, _data, k) => (id === _data.id ? k : _acc),
|
|
211
|
-
-1
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
return j === -1 ? acc : [i, j];
|
|
215
|
-
},
|
|
216
|
-
[-1, -1]
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function move_cursor(
|
|
221
|
-
key: "ArrowRight" | "ArrowLeft" | "ArrowDown" | "ArrowUp",
|
|
222
|
-
current_coords: [number, number]
|
|
223
|
-
): void {
|
|
224
|
-
const dir = {
|
|
225
|
-
ArrowRight: [0, 1],
|
|
226
|
-
ArrowLeft: [0, -1],
|
|
227
|
-
ArrowDown: [1, 0],
|
|
228
|
-
ArrowUp: [-1, 0]
|
|
229
|
-
}[key];
|
|
230
|
-
|
|
231
|
-
const i = current_coords[0] + dir[0];
|
|
232
|
-
const j = current_coords[1] + dir[1];
|
|
233
|
-
|
|
234
|
-
if (i < 0 && j <= 0) {
|
|
235
|
-
selected_header = j;
|
|
236
|
-
selected = false;
|
|
237
|
-
} else {
|
|
238
|
-
const is_data = data[i]?.[j];
|
|
239
|
-
selected = is_data ? [i, j] : selected;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
let clear_on_focus = false;
|
|
244
221
|
// eslint-disable-next-line complexity
|
|
245
222
|
async function handle_keydown(event: KeyboardEvent): Promise<void> {
|
|
246
223
|
if (selected_header !== false && header_edit === false) {
|
|
@@ -268,6 +245,50 @@
|
|
|
268
245
|
break;
|
|
269
246
|
}
|
|
270
247
|
}
|
|
248
|
+
|
|
249
|
+
if (event.key === "Delete" || event.key === "Backspace") {
|
|
250
|
+
if (!editable) return;
|
|
251
|
+
|
|
252
|
+
if (editing) {
|
|
253
|
+
const [row, col] = editing;
|
|
254
|
+
const input_el = els[data[row][col].id].input;
|
|
255
|
+
if (input_el && input_el.selectionStart !== input_el.selectionEnd) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
if (
|
|
259
|
+
event.key === "Delete" &&
|
|
260
|
+
input_el?.selectionStart !== input_el?.value.length
|
|
261
|
+
) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (event.key === "Backspace" && input_el?.selectionStart !== 0) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
event.preventDefault();
|
|
270
|
+
if (selected_cells.length > 0) {
|
|
271
|
+
data = handle_delete_key(data, selected_cells);
|
|
272
|
+
dispatch("change", {
|
|
273
|
+
data: data.map((row) => row.map((cell) => cell.value)),
|
|
274
|
+
headers: _headers.map((h) => h.value),
|
|
275
|
+
metadata: null
|
|
276
|
+
});
|
|
277
|
+
if (!value_is_output) {
|
|
278
|
+
dispatch("input");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (event.key === "c" && (event.metaKey || event.ctrlKey)) {
|
|
285
|
+
event.preventDefault();
|
|
286
|
+
if (selected_cells.length > 0) {
|
|
287
|
+
await handle_copy();
|
|
288
|
+
}
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
271
292
|
if (!selected) {
|
|
272
293
|
return;
|
|
273
294
|
}
|
|
@@ -281,7 +302,29 @@
|
|
|
281
302
|
case "ArrowUp":
|
|
282
303
|
if (editing) break;
|
|
283
304
|
event.preventDefault();
|
|
284
|
-
move_cursor(event.key, [i, j]);
|
|
305
|
+
const next_coords = move_cursor(event.key, [i, j], data);
|
|
306
|
+
if (next_coords) {
|
|
307
|
+
if (event.shiftKey) {
|
|
308
|
+
selected_cells = get_range_selection(
|
|
309
|
+
selected_cells.length > 0 ? selected_cells[0] : [i, j],
|
|
310
|
+
next_coords
|
|
311
|
+
);
|
|
312
|
+
editing = false;
|
|
313
|
+
} else {
|
|
314
|
+
selected_cells = [next_coords];
|
|
315
|
+
editing = next_coords;
|
|
316
|
+
clear_on_focus = false;
|
|
317
|
+
}
|
|
318
|
+
selected = next_coords;
|
|
319
|
+
} else if (
|
|
320
|
+
next_coords === false &&
|
|
321
|
+
event.key === "ArrowUp" &&
|
|
322
|
+
i === 0
|
|
323
|
+
) {
|
|
324
|
+
selected_header = j;
|
|
325
|
+
selected = false;
|
|
326
|
+
editing = false;
|
|
327
|
+
}
|
|
285
328
|
break;
|
|
286
329
|
|
|
287
330
|
case "Escape":
|
|
@@ -310,39 +353,26 @@
|
|
|
310
353
|
selected = [i, j];
|
|
311
354
|
} else {
|
|
312
355
|
editing = [i, j];
|
|
356
|
+
clear_on_focus = false;
|
|
313
357
|
}
|
|
314
358
|
}
|
|
315
|
-
|
|
316
|
-
break;
|
|
317
|
-
case "Backspace":
|
|
318
|
-
if (!editable) break;
|
|
319
|
-
if (!editing) {
|
|
320
|
-
event.preventDefault();
|
|
321
|
-
data[i][j].value = "";
|
|
322
|
-
}
|
|
323
|
-
break;
|
|
324
|
-
case "Delete":
|
|
325
|
-
if (!editable) break;
|
|
326
|
-
if (!editing) {
|
|
327
|
-
event.preventDefault();
|
|
328
|
-
data[i][j].value = "";
|
|
329
|
-
}
|
|
330
359
|
break;
|
|
331
360
|
case "Tab":
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
let is_data_x = data[i][j + direction];
|
|
335
|
-
let is_data_y =
|
|
336
|
-
data?.[i + direction]?.[direction > 0 ? 0 : _headers.length - 1];
|
|
337
|
-
|
|
338
|
-
if (is_data_x || is_data_y) {
|
|
339
|
-
event.preventDefault();
|
|
340
|
-
selected = is_data_x
|
|
341
|
-
? [i, j + direction]
|
|
342
|
-
: [i + direction, direction > 0 ? 0 : _headers.length - 1];
|
|
343
|
-
}
|
|
361
|
+
event.preventDefault();
|
|
344
362
|
editing = false;
|
|
345
|
-
|
|
363
|
+
const next_cell = get_next_cell_coordinates(
|
|
364
|
+
[i, j],
|
|
365
|
+
data,
|
|
366
|
+
event.shiftKey
|
|
367
|
+
);
|
|
368
|
+
if (next_cell) {
|
|
369
|
+
selected_cells = [next_cell];
|
|
370
|
+
selected = next_cell;
|
|
371
|
+
if (editable) {
|
|
372
|
+
editing = next_cell;
|
|
373
|
+
clear_on_focus = false;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
346
376
|
break;
|
|
347
377
|
default:
|
|
348
378
|
if (!editable) break;
|
|
@@ -373,16 +403,12 @@
|
|
|
373
403
|
}
|
|
374
404
|
}
|
|
375
405
|
|
|
376
|
-
let header_edit: number | false;
|
|
377
|
-
|
|
378
|
-
let select_on_focus = false;
|
|
379
|
-
let selected_header: number | false = false;
|
|
380
406
|
async function edit_header(i: number, _select = false): Promise<void> {
|
|
381
407
|
if (!editable || col_count[1] !== "dynamic" || header_edit === i) return;
|
|
382
408
|
selected = false;
|
|
409
|
+
selected_cells = [];
|
|
383
410
|
selected_header = i;
|
|
384
411
|
header_edit = i;
|
|
385
|
-
select_on_focus = _select;
|
|
386
412
|
}
|
|
387
413
|
|
|
388
414
|
function end_header_edit(event: CustomEvent<KeyboardEvent>): void {
|
|
@@ -457,107 +483,14 @@
|
|
|
457
483
|
}
|
|
458
484
|
|
|
459
485
|
function handle_click_outside(event: Event): void {
|
|
460
|
-
if (
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
) {
|
|
486
|
+
if (handle_click_outside_util(event, parent)) {
|
|
487
|
+
editing = false;
|
|
488
|
+
selected_cells = [];
|
|
489
|
+
header_edit = false;
|
|
490
|
+
selected_header = false;
|
|
466
491
|
active_cell_menu = null;
|
|
467
492
|
active_header_menu = null;
|
|
468
493
|
}
|
|
469
|
-
|
|
470
|
-
const [trigger] = event.composedPath() as HTMLElement[];
|
|
471
|
-
if (parent.contains(trigger)) {
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
clicked_cell = undefined;
|
|
476
|
-
editing = false;
|
|
477
|
-
selected = false;
|
|
478
|
-
header_edit = false;
|
|
479
|
-
selected_header = false;
|
|
480
|
-
active_cell_menu = null;
|
|
481
|
-
active_header_menu = null;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function guess_delimitaor(
|
|
485
|
-
text: string,
|
|
486
|
-
possibleDelimiters: string[]
|
|
487
|
-
): string[] {
|
|
488
|
-
return possibleDelimiters.filter(weedOut);
|
|
489
|
-
|
|
490
|
-
function weedOut(delimiter: string): boolean {
|
|
491
|
-
var cache = -1;
|
|
492
|
-
return text.split("\n").every(checkLength);
|
|
493
|
-
|
|
494
|
-
function checkLength(line: string): boolean {
|
|
495
|
-
if (!line) {
|
|
496
|
-
return true;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
var length = line.split(delimiter).length;
|
|
500
|
-
if (cache < 0) {
|
|
501
|
-
cache = length;
|
|
502
|
-
}
|
|
503
|
-
return cache === length && length > 1;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
function data_uri_to_blob(data_uri: string): Blob {
|
|
509
|
-
const byte_str = atob(data_uri.split(",")[1]);
|
|
510
|
-
const mime_str = data_uri.split(",")[0].split(":")[1].split(";")[0];
|
|
511
|
-
|
|
512
|
-
const ab = new ArrayBuffer(byte_str.length);
|
|
513
|
-
const ia = new Uint8Array(ab);
|
|
514
|
-
|
|
515
|
-
for (let i = 0; i < byte_str.length; i++) {
|
|
516
|
-
ia[i] = byte_str.charCodeAt(i);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
return new Blob([ab], { type: mime_str });
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function blob_to_string(blob: Blob): void {
|
|
523
|
-
const reader = new FileReader();
|
|
524
|
-
|
|
525
|
-
function handle_read(e: ProgressEvent<FileReader>): void {
|
|
526
|
-
if (!e?.target?.result || typeof e.target.result !== "string") return;
|
|
527
|
-
|
|
528
|
-
const [delimiter] = guess_delimitaor(e.target.result, [",", "\t"]);
|
|
529
|
-
|
|
530
|
-
const [head, ...rest] = dsvFormat(delimiter).parseRows(e.target.result);
|
|
531
|
-
|
|
532
|
-
_headers = make_headers(
|
|
533
|
-
col_count[1] === "fixed" ? head.slice(0, col_count[0]) : head
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
values = rest;
|
|
537
|
-
reader.removeEventListener("loadend", handle_read);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
reader.addEventListener("loadend", handle_read);
|
|
541
|
-
|
|
542
|
-
reader.readAsText(blob);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
let dragging = false;
|
|
546
|
-
|
|
547
|
-
function get_max(
|
|
548
|
-
_d: { value: any; id: string }[][]
|
|
549
|
-
): { value: any; id: string }[] {
|
|
550
|
-
if (!_d || _d.length === 0 || !_d[0]) return [];
|
|
551
|
-
let max = _d[0].slice();
|
|
552
|
-
for (let i = 0; i < _d.length; i++) {
|
|
553
|
-
for (let j = 0; j < _d[i].length; j++) {
|
|
554
|
-
if (`${max[j].value}`.length < `${_d[i][j].value}`.length) {
|
|
555
|
-
max[j] = _d[i][j];
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return max;
|
|
561
494
|
}
|
|
562
495
|
|
|
563
496
|
$: max = get_max(data);
|
|
@@ -628,7 +561,7 @@
|
|
|
628
561
|
data = data;
|
|
629
562
|
|
|
630
563
|
if (id) {
|
|
631
|
-
const [i, j] = get_current_indices(id);
|
|
564
|
+
const [i, j] = get_current_indices(id, data);
|
|
632
565
|
selected = [i, j];
|
|
633
566
|
}
|
|
634
567
|
}
|
|
@@ -640,19 +573,17 @@
|
|
|
640
573
|
let is_visible = false;
|
|
641
574
|
|
|
642
575
|
onMount(() => {
|
|
643
|
-
const observer = new IntersectionObserver((entries
|
|
576
|
+
const observer = new IntersectionObserver((entries) => {
|
|
644
577
|
entries.forEach((entry) => {
|
|
645
578
|
if (entry.isIntersecting && !is_visible) {
|
|
646
579
|
set_cell_widths();
|
|
647
580
|
data = data;
|
|
648
581
|
}
|
|
649
|
-
|
|
650
582
|
is_visible = entry.isIntersecting;
|
|
651
583
|
});
|
|
652
584
|
});
|
|
653
585
|
|
|
654
586
|
observer.observe(parent);
|
|
655
|
-
|
|
656
587
|
document.addEventListener("click", handle_click_outside);
|
|
657
588
|
window.addEventListener("resize", handle_resize);
|
|
658
589
|
document.addEventListener("fullscreenchange", handle_fullscreen_change);
|
|
@@ -668,14 +599,43 @@
|
|
|
668
599
|
};
|
|
669
600
|
});
|
|
670
601
|
|
|
671
|
-
|
|
602
|
+
function handle_cell_click(
|
|
603
|
+
event: MouseEvent,
|
|
604
|
+
row: number,
|
|
605
|
+
col: number
|
|
606
|
+
): void {
|
|
607
|
+
event.preventDefault();
|
|
608
|
+
event.stopPropagation();
|
|
609
|
+
clear_on_focus = false;
|
|
610
|
+
active_cell_menu = null;
|
|
611
|
+
active_header_menu = null;
|
|
612
|
+
selected_header = false;
|
|
613
|
+
header_edit = false;
|
|
672
614
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
615
|
+
selected_cells = handle_selection([row, col], selected_cells, event);
|
|
616
|
+
|
|
617
|
+
if (selected_cells.length === 1 && editable) {
|
|
618
|
+
editing = [row, col];
|
|
619
|
+
tick().then(() => {
|
|
620
|
+
const input_el = els[data[row][col].id].input;
|
|
621
|
+
if (input_el) {
|
|
622
|
+
input_el.focus();
|
|
623
|
+
input_el.selectionStart = input_el.selectionEnd =
|
|
624
|
+
input_el.value.length;
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
} else {
|
|
628
|
+
editing = false;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
toggle_cell_button(row, col);
|
|
632
|
+
|
|
633
|
+
dispatch("select", {
|
|
634
|
+
index: [row, col],
|
|
635
|
+
value: get_data_at(row, col),
|
|
636
|
+
row_value: data[row].map((d) => d.value)
|
|
637
|
+
});
|
|
638
|
+
}
|
|
679
639
|
|
|
680
640
|
function toggle_cell_menu(event: MouseEvent, row: number, col: number): void {
|
|
681
641
|
event.stopPropagation();
|
|
@@ -689,12 +649,7 @@
|
|
|
689
649
|
const cell = (event.target as HTMLElement).closest("td");
|
|
690
650
|
if (cell) {
|
|
691
651
|
const rect = cell.getBoundingClientRect();
|
|
692
|
-
active_cell_menu = {
|
|
693
|
-
row,
|
|
694
|
-
col,
|
|
695
|
-
x: rect.right,
|
|
696
|
-
y: rect.bottom
|
|
697
|
-
};
|
|
652
|
+
active_cell_menu = { row, col, x: rect.right, y: rect.bottom };
|
|
698
653
|
}
|
|
699
654
|
}
|
|
700
655
|
}
|
|
@@ -726,33 +681,21 @@
|
|
|
726
681
|
} | null = null;
|
|
727
682
|
|
|
728
683
|
function toggle_header_button(col: number): void {
|
|
729
|
-
|
|
730
|
-
active_button
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
684
|
+
active_button =
|
|
685
|
+
active_button?.type === "header" && active_button.col === col
|
|
686
|
+
? null
|
|
687
|
+
: { type: "header", col };
|
|
734
688
|
}
|
|
735
689
|
|
|
736
690
|
function toggle_cell_button(row: number, col: number): void {
|
|
737
|
-
|
|
691
|
+
active_button =
|
|
738
692
|
active_button?.type === "cell" &&
|
|
739
693
|
active_button.row === row &&
|
|
740
694
|
active_button.col === col
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
} else {
|
|
744
|
-
active_button = { type: "cell", row, col };
|
|
745
|
-
}
|
|
695
|
+
? null
|
|
696
|
+
: { type: "cell", row, col };
|
|
746
697
|
}
|
|
747
698
|
|
|
748
|
-
let active_header_menu: {
|
|
749
|
-
col: number;
|
|
750
|
-
x: number;
|
|
751
|
-
y: number;
|
|
752
|
-
} | null = null;
|
|
753
|
-
|
|
754
|
-
let is_fullscreen = false;
|
|
755
|
-
|
|
756
699
|
function toggle_fullscreen(): void {
|
|
757
700
|
if (!document.fullscreenElement) {
|
|
758
701
|
parent.requestFullscreen();
|
|
@@ -768,7 +711,7 @@
|
|
|
768
711
|
}
|
|
769
712
|
|
|
770
713
|
async function handle_copy(): Promise<void> {
|
|
771
|
-
await copy_table_data(data, _headers);
|
|
714
|
+
await copy_table_data(data, _headers, selected_cells);
|
|
772
715
|
}
|
|
773
716
|
|
|
774
717
|
function toggle_header_menu(event: MouseEvent, col: number): void {
|
|
@@ -779,11 +722,7 @@
|
|
|
779
722
|
const header = (event.target as HTMLElement).closest("th");
|
|
780
723
|
if (header) {
|
|
781
724
|
const rect = header.getBoundingClientRect();
|
|
782
|
-
active_header_menu = {
|
|
783
|
-
col,
|
|
784
|
-
x: rect.right,
|
|
785
|
-
y: rect.bottom
|
|
786
|
-
};
|
|
725
|
+
active_header_menu = { col, x: rect.right, y: rect.bottom };
|
|
787
726
|
}
|
|
788
727
|
}
|
|
789
728
|
}
|
|
@@ -884,6 +823,7 @@
|
|
|
884
823
|
edit={false}
|
|
885
824
|
el={null}
|
|
886
825
|
{root}
|
|
826
|
+
{editable}
|
|
887
827
|
/>
|
|
888
828
|
|
|
889
829
|
<div
|
|
@@ -919,6 +859,7 @@
|
|
|
919
859
|
edit={false}
|
|
920
860
|
el={null}
|
|
921
861
|
{root}
|
|
862
|
+
{editable}
|
|
922
863
|
/>
|
|
923
864
|
</div>
|
|
924
865
|
</td>
|
|
@@ -934,8 +875,20 @@
|
|
|
934
875
|
boundedheight={false}
|
|
935
876
|
disable_click={true}
|
|
936
877
|
{root}
|
|
937
|
-
on:load={(
|
|
878
|
+
on:load={({ detail }) =>
|
|
879
|
+
handle_file_upload(
|
|
880
|
+
detail.data,
|
|
881
|
+
col_count,
|
|
882
|
+
(head) => {
|
|
883
|
+
_headers = make_headers(head);
|
|
884
|
+
return _headers;
|
|
885
|
+
},
|
|
886
|
+
(vals) => {
|
|
887
|
+
values = vals;
|
|
888
|
+
}
|
|
889
|
+
)}
|
|
938
890
|
bind:dragging
|
|
891
|
+
aria_label={i18n("dataframe.drop_to_upload")}
|
|
939
892
|
>
|
|
940
893
|
<VirtualTable
|
|
941
894
|
bind:items={data}
|
|
@@ -963,6 +916,7 @@
|
|
|
963
916
|
<div class="cell-wrap">
|
|
964
917
|
<div class="header-content">
|
|
965
918
|
<EditableCell
|
|
919
|
+
{max_chars}
|
|
966
920
|
bind:value={_headers[i].value}
|
|
967
921
|
bind:el={els[id].input}
|
|
968
922
|
{latex_delimiters}
|
|
@@ -972,6 +926,7 @@
|
|
|
972
926
|
on:dblclick={() => edit_header(i)}
|
|
973
927
|
header
|
|
974
928
|
{root}
|
|
929
|
+
{editable}
|
|
975
930
|
/>
|
|
976
931
|
<button
|
|
977
932
|
class:sorted={sort_by === i}
|
|
@@ -1000,7 +955,7 @@
|
|
|
1000
955
|
class="cell-menu-button"
|
|
1001
956
|
on:click={(event) => toggle_header_menu(event, i)}
|
|
1002
957
|
>
|
|
1003
|
-
|
|
958
|
+
⋮
|
|
1004
959
|
</button>
|
|
1005
960
|
{/if}
|
|
1006
961
|
</div>
|
|
@@ -1016,40 +971,24 @@
|
|
|
1016
971
|
<td
|
|
1017
972
|
tabindex="0"
|
|
1018
973
|
on:touchstart={(event) => {
|
|
1019
|
-
event.
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1029
|
-
toggle_cell_button(index, j);
|
|
974
|
+
const touch = event.touches[0];
|
|
975
|
+
const mouseEvent = new MouseEvent("click", {
|
|
976
|
+
clientX: touch.clientX,
|
|
977
|
+
clientY: touch.clientY,
|
|
978
|
+
bubbles: true,
|
|
979
|
+
cancelable: true,
|
|
980
|
+
view: window
|
|
981
|
+
});
|
|
982
|
+
handle_cell_click(mouseEvent, index, j);
|
|
1030
983
|
}}
|
|
1031
984
|
on:mousedown={(event) => {
|
|
1032
985
|
event.preventDefault();
|
|
1033
986
|
event.stopPropagation();
|
|
1034
987
|
}}
|
|
1035
|
-
on:click={(event) =>
|
|
1036
|
-
event.preventDefault();
|
|
1037
|
-
event.stopPropagation();
|
|
1038
|
-
clear_on_focus = false;
|
|
1039
|
-
active_cell_menu = null;
|
|
1040
|
-
active_header_menu = null;
|
|
1041
|
-
clicked_cell = { row: index, col: j };
|
|
1042
|
-
selected = [index, j];
|
|
1043
|
-
selected_header = false;
|
|
1044
|
-
header_edit = false;
|
|
1045
|
-
if (editable) {
|
|
1046
|
-
editing = [index, j];
|
|
1047
|
-
}
|
|
1048
|
-
toggle_cell_button(index, j);
|
|
1049
|
-
}}
|
|
988
|
+
on:click={(event) => handle_cell_click(event, index, j)}
|
|
1050
989
|
style:width="var(--cell-width-{j})"
|
|
1051
990
|
style={styling?.[index]?.[j] || ""}
|
|
1052
|
-
class
|
|
991
|
+
class={is_cell_selected([index, j], selected_cells)}
|
|
1053
992
|
class:menu-active={active_cell_menu &&
|
|
1054
993
|
active_cell_menu.row === index &&
|
|
1055
994
|
active_cell_menu.col === j}
|
|
@@ -1068,15 +1007,25 @@
|
|
|
1068
1007
|
clear_on_focus = false;
|
|
1069
1008
|
parent.focus();
|
|
1070
1009
|
}}
|
|
1010
|
+
on:focus={() => {
|
|
1011
|
+
const row = index;
|
|
1012
|
+
const col = j;
|
|
1013
|
+
if (
|
|
1014
|
+
!selected_cells.some(([r, c]) => r === row && c === col)
|
|
1015
|
+
) {
|
|
1016
|
+
selected_cells = [[row, col]];
|
|
1017
|
+
}
|
|
1018
|
+
}}
|
|
1071
1019
|
{clear_on_focus}
|
|
1072
1020
|
{root}
|
|
1021
|
+
{max_chars}
|
|
1073
1022
|
/>
|
|
1074
|
-
{#if editable}
|
|
1023
|
+
{#if editable && should_show_cell_menu([index, j], selected_cells, editable)}
|
|
1075
1024
|
<button
|
|
1076
1025
|
class="cell-menu-button"
|
|
1077
1026
|
on:click={(event) => toggle_cell_menu(event, index, j)}
|
|
1078
1027
|
>
|
|
1079
|
-
|
|
1028
|
+
⋮
|
|
1080
1029
|
</button>
|
|
1081
1030
|
{/if}
|
|
1082
1031
|
</div>
|
|
@@ -1128,21 +1077,10 @@
|
|
|
1128
1077
|
{/if}
|
|
1129
1078
|
|
|
1130
1079
|
<style>
|
|
1131
|
-
.
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
.button-wrap svg {
|
|
1136
|
-
margin-right: var(--size-1);
|
|
1137
|
-
margin-left: -5px;
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
.label p {
|
|
1141
|
-
position: relative;
|
|
1142
|
-
z-index: var(--layer-4);
|
|
1143
|
-
margin-bottom: var(--size-2);
|
|
1144
|
-
color: var(--block-label-text-color);
|
|
1145
|
-
font-size: var(--block-label-text-size);
|
|
1080
|
+
.table-container {
|
|
1081
|
+
display: flex;
|
|
1082
|
+
flex-direction: column;
|
|
1083
|
+
gap: var(--size-2);
|
|
1146
1084
|
}
|
|
1147
1085
|
|
|
1148
1086
|
.table-wrap {
|
|
@@ -1155,7 +1093,6 @@
|
|
|
1155
1093
|
|
|
1156
1094
|
.table-wrap:focus-within {
|
|
1157
1095
|
outline: none;
|
|
1158
|
-
background-color: none;
|
|
1159
1096
|
}
|
|
1160
1097
|
|
|
1161
1098
|
.dragging {
|
|
@@ -1177,6 +1114,7 @@
|
|
|
1177
1114
|
line-height: var(--line-md);
|
|
1178
1115
|
font-family: var(--font-mono);
|
|
1179
1116
|
border-spacing: 0;
|
|
1117
|
+
border-collapse: separate;
|
|
1180
1118
|
}
|
|
1181
1119
|
|
|
1182
1120
|
div:not(.no-wrap) td {
|
|
@@ -1231,6 +1169,12 @@
|
|
|
1231
1169
|
th.focus,
|
|
1232
1170
|
td.focus {
|
|
1233
1171
|
--ring-color: var(--color-accent);
|
|
1172
|
+
box-shadow: inset 0 0 0 2px var(--ring-color);
|
|
1173
|
+
z-index: var(--layer-1);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
th.focus {
|
|
1177
|
+
z-index: var(--layer-2);
|
|
1234
1178
|
}
|
|
1235
1179
|
|
|
1236
1180
|
tr:last-child td:first-child {
|
|
@@ -1259,7 +1203,6 @@
|
|
|
1259
1203
|
cursor: pointer;
|
|
1260
1204
|
padding: var(--size-2);
|
|
1261
1205
|
color: var(--body-text-color-subdued);
|
|
1262
|
-
line-height: var(--text-sm);
|
|
1263
1206
|
}
|
|
1264
1207
|
|
|
1265
1208
|
.sort-button:hover {
|
|
@@ -1280,11 +1223,11 @@
|
|
|
1280
1223
|
|
|
1281
1224
|
.cell-wrap {
|
|
1282
1225
|
display: flex;
|
|
1283
|
-
align-items:
|
|
1226
|
+
align-items: flex-start;
|
|
1284
1227
|
outline: none;
|
|
1285
|
-
height: var(--size-full);
|
|
1286
1228
|
min-height: var(--size-9);
|
|
1287
|
-
|
|
1229
|
+
position: relative;
|
|
1230
|
+
height: auto;
|
|
1288
1231
|
}
|
|
1289
1232
|
|
|
1290
1233
|
.header-content {
|
|
@@ -1293,12 +1236,9 @@
|
|
|
1293
1236
|
overflow: hidden;
|
|
1294
1237
|
flex-grow: 1;
|
|
1295
1238
|
min-width: 0;
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
display: flex;
|
|
1300
|
-
justify-content: flex-end;
|
|
1301
|
-
padding-top: var(--size-2);
|
|
1239
|
+
white-space: normal;
|
|
1240
|
+
overflow-wrap: break-word;
|
|
1241
|
+
word-break: break-word;
|
|
1302
1242
|
}
|
|
1303
1243
|
|
|
1304
1244
|
.row_odd {
|
|
@@ -1309,10 +1249,6 @@
|
|
|
1309
1249
|
background: var(--background-fill-primary);
|
|
1310
1250
|
}
|
|
1311
1251
|
|
|
1312
|
-
table {
|
|
1313
|
-
border-collapse: separate;
|
|
1314
|
-
}
|
|
1315
|
-
|
|
1316
1252
|
.cell-menu-button {
|
|
1317
1253
|
flex-shrink: 0;
|
|
1318
1254
|
display: none;
|
|
@@ -1325,28 +1261,35 @@
|
|
|
1325
1261
|
padding: 0;
|
|
1326
1262
|
margin-right: var(--spacing-sm);
|
|
1327
1263
|
z-index: var(--layer-1);
|
|
1264
|
+
position: absolute;
|
|
1265
|
+
right: var(--size-1);
|
|
1266
|
+
top: 50%;
|
|
1267
|
+
transform: translateY(-50%);
|
|
1328
1268
|
}
|
|
1329
1269
|
|
|
1330
|
-
.cell-menu-button
|
|
1331
|
-
|
|
1270
|
+
.cell-selected .cell-menu-button {
|
|
1271
|
+
display: flex;
|
|
1272
|
+
align-items: center;
|
|
1273
|
+
justify-content: center;
|
|
1332
1274
|
}
|
|
1333
1275
|
|
|
1334
|
-
|
|
1276
|
+
.header-row {
|
|
1335
1277
|
display: flex;
|
|
1278
|
+
justify-content: space-between;
|
|
1336
1279
|
align-items: center;
|
|
1337
|
-
|
|
1280
|
+
gap: var(--size-2);
|
|
1281
|
+
height: var(--size-6);
|
|
1282
|
+
min-height: var(--size-6);
|
|
1338
1283
|
}
|
|
1339
1284
|
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
overflow-wrap: break-word;
|
|
1343
|
-
word-break: break-word;
|
|
1285
|
+
.label {
|
|
1286
|
+
flex: 1;
|
|
1344
1287
|
}
|
|
1345
1288
|
|
|
1346
|
-
.
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1289
|
+
.label p {
|
|
1290
|
+
margin: 0;
|
|
1291
|
+
color: var(--block-label-text-color);
|
|
1292
|
+
font-size: var(--block-label-text-size);
|
|
1350
1293
|
}
|
|
1351
1294
|
|
|
1352
1295
|
.row-number,
|
|
@@ -1378,24 +1321,94 @@
|
|
|
1378
1321
|
background: var(--table-odd-background-fill);
|
|
1379
1322
|
}
|
|
1380
1323
|
|
|
1381
|
-
.
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
height: var(--size-6);
|
|
1387
|
-
min-height: var(--size-6);
|
|
1324
|
+
.cell-selected {
|
|
1325
|
+
--ring-color: var(--color-accent);
|
|
1326
|
+
box-shadow: inset 0 0 0 2px var(--ring-color);
|
|
1327
|
+
z-index: var(--layer-1);
|
|
1328
|
+
position: relative;
|
|
1388
1329
|
}
|
|
1389
1330
|
|
|
1390
|
-
.
|
|
1391
|
-
|
|
1331
|
+
.cell-selected.no-top {
|
|
1332
|
+
box-shadow:
|
|
1333
|
+
inset 2px 0 0 var(--ring-color),
|
|
1334
|
+
inset -2px 0 0 var(--ring-color),
|
|
1335
|
+
inset 0 -2px 0 var(--ring-color);
|
|
1392
1336
|
}
|
|
1393
1337
|
|
|
1394
|
-
.
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1338
|
+
.cell-selected.no-bottom {
|
|
1339
|
+
box-shadow:
|
|
1340
|
+
inset 2px 0 0 var(--ring-color),
|
|
1341
|
+
inset -2px 0 0 var(--ring-color),
|
|
1342
|
+
inset 0 2px 0 var(--ring-color);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
.cell-selected.no-left {
|
|
1346
|
+
box-shadow:
|
|
1347
|
+
inset 0 2px 0 var(--ring-color),
|
|
1348
|
+
inset -2px 0 0 var(--ring-color),
|
|
1349
|
+
inset 0 -2px 0 var(--ring-color);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
.cell-selected.no-right {
|
|
1353
|
+
box-shadow:
|
|
1354
|
+
inset 0 2px 0 var(--ring-color),
|
|
1355
|
+
inset 2px 0 0 var(--ring-color),
|
|
1356
|
+
inset 0 -2px 0 var(--ring-color);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
.cell-selected.no-top.no-left {
|
|
1360
|
+
box-shadow:
|
|
1361
|
+
inset -2px 0 0 var(--ring-color),
|
|
1362
|
+
inset 0 -2px 0 var(--ring-color);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
.cell-selected.no-top.no-right {
|
|
1366
|
+
box-shadow:
|
|
1367
|
+
inset 2px 0 0 var(--ring-color),
|
|
1368
|
+
inset 0 -2px 0 var(--ring-color);
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.cell-selected.no-bottom.no-left {
|
|
1372
|
+
box-shadow:
|
|
1373
|
+
inset -2px 0 0 var(--ring-color),
|
|
1374
|
+
inset 0 2px 0 var(--ring-color);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
.cell-selected.no-bottom.no-right {
|
|
1378
|
+
box-shadow:
|
|
1379
|
+
inset 2px 0 0 var(--ring-color),
|
|
1380
|
+
inset 0 2px 0 var(--ring-color);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.cell-selected.no-top.no-bottom {
|
|
1384
|
+
box-shadow:
|
|
1385
|
+
inset 2px 0 0 var(--ring-color),
|
|
1386
|
+
inset -2px 0 0 var(--ring-color);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
.cell-selected.no-left.no-right {
|
|
1390
|
+
box-shadow:
|
|
1391
|
+
inset 0 2px 0 var(--ring-color),
|
|
1392
|
+
inset 0 -2px 0 var(--ring-color);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.cell-selected.no-top.no-left.no-right {
|
|
1396
|
+
box-shadow: inset 0 -2px 0 var(--ring-color);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
.cell-selected.no-bottom.no-left.no-right {
|
|
1400
|
+
box-shadow: inset 0 2px 0 var(--ring-color);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
.cell-selected.no-left.no-top.no-bottom {
|
|
1404
|
+
box-shadow: inset -2px 0 0 var(--ring-color);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
.cell-selected.no-right.no-top.no-bottom {
|
|
1408
|
+
box-shadow: inset 2px 0 0 var(--ring-color);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
.cell-selected.no-top.no-bottom.no-left.no-right {
|
|
1412
|
+
box-shadow: none;
|
|
1400
1413
|
}
|
|
1401
1414
|
</style>
|