@gradio/dataframe 0.9.2 → 0.10.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,915 @@
1
+ <script>import { createEventDispatcher, tick, onMount } from "svelte";
2
+ import { dsvFormat } from "d3-dsv";
3
+ import { dequal } from "dequal/lite";
4
+ import { copy } from "@gradio/utils";
5
+ import { Upload } from "@gradio/upload";
6
+ import { BaseButton } from "@gradio/button";
7
+ import EditableCell from "./EditableCell.svelte";
8
+ import {} from "@gradio/client";
9
+ import VirtualTable from "./VirtualTable.svelte";
10
+ export let datatype;
11
+ export let label = null;
12
+ export let show_label = true;
13
+ export let headers = [];
14
+ export let values = [];
15
+ export let col_count;
16
+ export let row_count;
17
+ export let latex_delimiters;
18
+ export let editable = true;
19
+ export let wrap = false;
20
+ export let root;
21
+ export let i18n;
22
+ export let height = 500;
23
+ export let line_breaks = true;
24
+ export let column_widths = [];
25
+ export let upload;
26
+ export let stream_handler;
27
+ let selected = false;
28
+ export let display_value = null;
29
+ export let styling = null;
30
+ let t_rect;
31
+ const dispatch = createEventDispatcher();
32
+ let editing = false;
33
+ const get_data_at = (row, col) => data?.[row]?.[col]?.value;
34
+ $: {
35
+ if (selected !== false) {
36
+ const [row, col] = selected;
37
+ if (!isNaN(row) && !isNaN(col)) {
38
+ dispatch("select", {
39
+ index: [row, col],
40
+ value: get_data_at(row, col),
41
+ row_value: data[row].map((d) => d.value)
42
+ });
43
+ }
44
+ }
45
+ }
46
+ let els = {};
47
+ let data_binding = {};
48
+ function make_id() {
49
+ return Math.random().toString(36).substring(2, 15);
50
+ }
51
+ function make_headers(_head) {
52
+ let _h = _head || [];
53
+ if (col_count[1] === "fixed" && _h.length < col_count[0]) {
54
+ const fill = Array(col_count[0] - _h.length).fill("").map((_, i) => `${i + _h.length}`);
55
+ _h = _h.concat(fill);
56
+ }
57
+ if (!_h || _h.length === 0) {
58
+ return Array(col_count[0]).fill(0).map((_, i) => {
59
+ const _id = make_id();
60
+ els[_id] = { cell: null, input: null };
61
+ return { id: _id, value: JSON.stringify(i + 1) };
62
+ });
63
+ }
64
+ return _h.map((h, i) => {
65
+ const _id = make_id();
66
+ els[_id] = { cell: null, input: null };
67
+ return { id: _id, value: h ?? "" };
68
+ });
69
+ }
70
+ function process_data(_values) {
71
+ const data_row_length = _values.length;
72
+ return Array(
73
+ row_count[1] === "fixed" ? row_count[0] : data_row_length < row_count[0] ? row_count[0] : data_row_length
74
+ ).fill(0).map(
75
+ (_, i) => Array(
76
+ col_count[1] === "fixed" ? col_count[0] : data_row_length > 0 ? _values[0].length : headers.length
77
+ ).fill(0).map((_2, j) => {
78
+ const id = make_id();
79
+ els[id] = els[id] || { input: null, cell: null };
80
+ const obj = { value: _values?.[i]?.[j] ?? "", id };
81
+ data_binding[id] = obj;
82
+ return obj;
83
+ })
84
+ );
85
+ }
86
+ let _headers = make_headers(headers);
87
+ let old_headers;
88
+ $: {
89
+ if (!dequal(headers, old_headers)) {
90
+ trigger_headers();
91
+ }
92
+ }
93
+ function trigger_headers() {
94
+ _headers = make_headers(headers);
95
+ old_headers = headers.slice();
96
+ trigger_change();
97
+ }
98
+ $:
99
+ if (!dequal(values, old_val)) {
100
+ data = process_data(values);
101
+ old_val = values;
102
+ }
103
+ let data = [[]];
104
+ let old_val = void 0;
105
+ async function trigger_change() {
106
+ dispatch("change", {
107
+ data: data.map((r) => r.map(({ value }) => value)),
108
+ headers: _headers.map((h) => h.value),
109
+ metadata: editable ? null : { display_value, styling }
110
+ });
111
+ }
112
+ function get_sort_status(name, _sort, direction) {
113
+ if (!_sort)
114
+ return "none";
115
+ if (headers[_sort] === name) {
116
+ if (direction === "asc")
117
+ return "ascending";
118
+ if (direction === "des")
119
+ return "descending";
120
+ }
121
+ return "none";
122
+ }
123
+ function get_current_indices(id) {
124
+ return data.reduce(
125
+ (acc, arr, i) => {
126
+ const j = arr.reduce(
127
+ (_acc, _data, k) => id === _data.id ? k : _acc,
128
+ -1
129
+ );
130
+ return j === -1 ? acc : [i, j];
131
+ },
132
+ [-1, -1]
133
+ );
134
+ }
135
+ async function start_edit(i, j) {
136
+ if (!editable || dequal(editing, [i, j]))
137
+ return;
138
+ editing = [i, j];
139
+ }
140
+ function move_cursor(key, current_coords) {
141
+ const dir = {
142
+ ArrowRight: [0, 1],
143
+ ArrowLeft: [0, -1],
144
+ ArrowDown: [1, 0],
145
+ ArrowUp: [-1, 0]
146
+ }[key];
147
+ const i = current_coords[0] + dir[0];
148
+ const j = current_coords[1] + dir[1];
149
+ if (i < 0 && j <= 0) {
150
+ selected_header = j;
151
+ selected = false;
152
+ } else {
153
+ const is_data = data[i]?.[j];
154
+ selected = is_data ? [i, j] : selected;
155
+ }
156
+ }
157
+ let clear_on_focus = false;
158
+ async function handle_keydown(event) {
159
+ if (selected_header !== false && header_edit === false) {
160
+ switch (event.key) {
161
+ case "ArrowDown":
162
+ selected = [0, selected_header];
163
+ selected_header = false;
164
+ return;
165
+ case "ArrowLeft":
166
+ selected_header = selected_header > 0 ? selected_header - 1 : selected_header;
167
+ return;
168
+ case "ArrowRight":
169
+ selected_header = selected_header < _headers.length - 1 ? selected_header + 1 : selected_header;
170
+ return;
171
+ case "Escape":
172
+ event.preventDefault();
173
+ selected_header = false;
174
+ break;
175
+ case "Enter":
176
+ event.preventDefault();
177
+ break;
178
+ }
179
+ }
180
+ if (!selected) {
181
+ return;
182
+ }
183
+ const [i, j] = selected;
184
+ switch (event.key) {
185
+ case "ArrowRight":
186
+ case "ArrowLeft":
187
+ case "ArrowDown":
188
+ case "ArrowUp":
189
+ if (editing)
190
+ break;
191
+ event.preventDefault();
192
+ move_cursor(event.key, [i, j]);
193
+ break;
194
+ case "Escape":
195
+ if (!editable)
196
+ break;
197
+ event.preventDefault();
198
+ editing = false;
199
+ break;
200
+ case "Enter":
201
+ if (!editable)
202
+ break;
203
+ event.preventDefault();
204
+ if (event.shiftKey) {
205
+ add_row(i);
206
+ await tick();
207
+ selected = [i + 1, j];
208
+ } else {
209
+ if (dequal(editing, [i, j])) {
210
+ editing = false;
211
+ await tick();
212
+ selected = [i, j];
213
+ } else {
214
+ editing = [i, j];
215
+ }
216
+ }
217
+ break;
218
+ case "Backspace":
219
+ if (!editable)
220
+ break;
221
+ if (!editing) {
222
+ event.preventDefault();
223
+ data[i][j].value = "";
224
+ }
225
+ break;
226
+ case "Delete":
227
+ if (!editable)
228
+ break;
229
+ if (!editing) {
230
+ event.preventDefault();
231
+ data[i][j].value = "";
232
+ }
233
+ break;
234
+ case "Tab":
235
+ let direction = event.shiftKey ? -1 : 1;
236
+ let is_data_x = data[i][j + direction];
237
+ let is_data_y = data?.[i + direction]?.[direction > 0 ? 0 : _headers.length - 1];
238
+ if (is_data_x || is_data_y) {
239
+ event.preventDefault();
240
+ selected = is_data_x ? [i, j + direction] : [i + direction, direction > 0 ? 0 : _headers.length - 1];
241
+ }
242
+ editing = false;
243
+ break;
244
+ default:
245
+ if (!editable)
246
+ break;
247
+ if ((!editing || editing && dequal(editing, [i, j])) && event.key.length === 1) {
248
+ clear_on_focus = true;
249
+ editing = [i, j];
250
+ }
251
+ }
252
+ }
253
+ async function handle_cell_click(i, j) {
254
+ if (dequal(editing, [i, j]))
255
+ return;
256
+ header_edit = false;
257
+ selected_header = false;
258
+ editing = false;
259
+ selected = [i, j];
260
+ await tick();
261
+ parent.focus();
262
+ }
263
+ let sort_direction;
264
+ let sort_by;
265
+ function handle_sort(col) {
266
+ if (typeof sort_by !== "number" || sort_by !== col) {
267
+ sort_direction = "asc";
268
+ sort_by = col;
269
+ } else {
270
+ if (sort_direction === "asc") {
271
+ sort_direction = "des";
272
+ } else if (sort_direction === "des") {
273
+ sort_direction = "asc";
274
+ }
275
+ }
276
+ }
277
+ let header_edit;
278
+ let select_on_focus = false;
279
+ let selected_header = false;
280
+ async function edit_header(i, _select = false) {
281
+ if (!editable || col_count[1] !== "dynamic" || header_edit === i)
282
+ return;
283
+ selected = false;
284
+ selected_header = i;
285
+ header_edit = i;
286
+ select_on_focus = _select;
287
+ }
288
+ function end_header_edit(event) {
289
+ if (!editable)
290
+ return;
291
+ switch (event.key) {
292
+ case "Escape":
293
+ case "Enter":
294
+ case "Tab":
295
+ event.preventDefault();
296
+ selected = false;
297
+ selected_header = header_edit;
298
+ header_edit = false;
299
+ parent.focus();
300
+ break;
301
+ }
302
+ }
303
+ async function add_row(index) {
304
+ parent.focus();
305
+ if (row_count[1] !== "dynamic")
306
+ return;
307
+ if (data.length === 0) {
308
+ values = [Array(headers.length).fill("")];
309
+ return;
310
+ }
311
+ data.splice(
312
+ index ? index + 1 : data.length,
313
+ 0,
314
+ Array(data[0].length).fill(0).map((_, i) => {
315
+ const _id = make_id();
316
+ els[_id] = { cell: null, input: null };
317
+ return { id: _id, value: "" };
318
+ })
319
+ );
320
+ data = data;
321
+ selected = [index ? index + 1 : data.length - 1, 0];
322
+ }
323
+ $:
324
+ data && trigger_change();
325
+ async function add_col() {
326
+ parent.focus();
327
+ if (col_count[1] !== "dynamic")
328
+ return;
329
+ for (let i = 0; i < data.length; i++) {
330
+ const _id = make_id();
331
+ els[_id] = { cell: null, input: null };
332
+ data[i].push({ id: _id, value: "" });
333
+ }
334
+ headers.push(`Header ${headers.length + 1}`);
335
+ data = data;
336
+ headers = headers;
337
+ await tick();
338
+ requestAnimationFrame(() => {
339
+ edit_header(headers.length - 1, true);
340
+ const new_w = parent.querySelectorAll("tbody")[1].offsetWidth;
341
+ parent.querySelectorAll("table")[1].scrollTo({ left: new_w });
342
+ });
343
+ }
344
+ function handle_click_outside(event) {
345
+ event.stopImmediatePropagation();
346
+ const [trigger] = event.composedPath();
347
+ if (parent.contains(trigger)) {
348
+ return;
349
+ }
350
+ editing = false;
351
+ header_edit = false;
352
+ selected_header = false;
353
+ selected = false;
354
+ }
355
+ function guess_delimitaor(text, possibleDelimiters) {
356
+ return possibleDelimiters.filter(weedOut);
357
+ function weedOut(delimiter) {
358
+ var cache = -1;
359
+ return text.split("\n").every(checkLength);
360
+ function checkLength(line) {
361
+ if (!line) {
362
+ return true;
363
+ }
364
+ var length = line.split(delimiter).length;
365
+ if (cache < 0) {
366
+ cache = length;
367
+ }
368
+ return cache === length && length > 1;
369
+ }
370
+ }
371
+ }
372
+ function data_uri_to_blob(data_uri) {
373
+ const byte_str = atob(data_uri.split(",")[1]);
374
+ const mime_str = data_uri.split(",")[0].split(":")[1].split(";")[0];
375
+ const ab = new ArrayBuffer(byte_str.length);
376
+ const ia = new Uint8Array(ab);
377
+ for (let i = 0; i < byte_str.length; i++) {
378
+ ia[i] = byte_str.charCodeAt(i);
379
+ }
380
+ return new Blob([ab], { type: mime_str });
381
+ }
382
+ function blob_to_string(blob) {
383
+ const reader = new FileReader();
384
+ function handle_read(e) {
385
+ if (!e?.target?.result || typeof e.target.result !== "string")
386
+ return;
387
+ const [delimiter] = guess_delimitaor(e.target.result, [",", " "]);
388
+ const [head, ...rest] = dsvFormat(delimiter).parseRows(e.target.result);
389
+ _headers = make_headers(
390
+ col_count[1] === "fixed" ? head.slice(0, col_count[0]) : head
391
+ );
392
+ values = rest;
393
+ reader.removeEventListener("loadend", handle_read);
394
+ }
395
+ reader.addEventListener("loadend", handle_read);
396
+ reader.readAsText(blob);
397
+ }
398
+ let dragging = false;
399
+ function get_max(_d) {
400
+ let max2 = _d[0].slice();
401
+ for (let i = 0; i < _d.length; i++) {
402
+ for (let j = 0; j < _d[i].length; j++) {
403
+ if (`${max2[j].value}`.length < `${_d[i][j].value}`.length) {
404
+ max2[j] = _d[i][j];
405
+ }
406
+ }
407
+ }
408
+ return max2;
409
+ }
410
+ $:
411
+ max = get_max(data);
412
+ $:
413
+ cells[0] && set_cell_widths();
414
+ let cells = [];
415
+ let parent;
416
+ let table;
417
+ function set_cell_widths() {
418
+ const widths = cells.map((el, i) => {
419
+ return el?.clientWidth || 0;
420
+ });
421
+ if (widths.length === 0)
422
+ return;
423
+ for (let i = 0; i < widths.length; i++) {
424
+ parent.style.setProperty(
425
+ `--cell-width-${i}`,
426
+ `${widths[i] - scrollbar_width / widths.length}px`
427
+ );
428
+ }
429
+ }
430
+ let table_height = values.slice(0, height / values.length * 37).length * 37 + 37;
431
+ let scrollbar_width = 0;
432
+ function sort_data(_data, _display_value, _styling, col, dir) {
433
+ let id = null;
434
+ if (selected && selected[0] in data && selected[1] in data[selected[0]]) {
435
+ id = data[selected[0]][selected[1]].id;
436
+ }
437
+ if (typeof col !== "number" || !dir) {
438
+ return;
439
+ }
440
+ const indices = [...Array(_data.length).keys()];
441
+ if (dir === "asc") {
442
+ indices.sort(
443
+ (i, j) => _data[i][col].value < _data[j][col].value ? -1 : 1
444
+ );
445
+ } else if (dir === "des") {
446
+ indices.sort(
447
+ (i, j) => _data[i][col].value > _data[j][col].value ? -1 : 1
448
+ );
449
+ } else {
450
+ return;
451
+ }
452
+ const temp_data = [..._data];
453
+ const temp_display_value = _display_value ? [..._display_value] : null;
454
+ const temp_styling = _styling ? [..._styling] : null;
455
+ indices.forEach((originalIndex, sortedIndex) => {
456
+ _data[sortedIndex] = temp_data[originalIndex];
457
+ if (_display_value && temp_display_value)
458
+ _display_value[sortedIndex] = temp_display_value[originalIndex];
459
+ if (_styling && temp_styling)
460
+ _styling[sortedIndex] = temp_styling[originalIndex];
461
+ });
462
+ data = data;
463
+ if (id) {
464
+ const [i, j] = get_current_indices(id);
465
+ selected = [i, j];
466
+ }
467
+ }
468
+ $:
469
+ sort_data(data, display_value, styling, sort_by, sort_direction);
470
+ $:
471
+ selected_index = !!selected && selected[0];
472
+ let is_visible = false;
473
+ onMount(() => {
474
+ const observer = new IntersectionObserver((entries, observer2) => {
475
+ entries.forEach((entry) => {
476
+ if (entry.isIntersecting && !is_visible) {
477
+ set_cell_widths();
478
+ data = data;
479
+ }
480
+ is_visible = entry.isIntersecting;
481
+ });
482
+ });
483
+ observer.observe(parent);
484
+ return () => {
485
+ observer.disconnect();
486
+ };
487
+ });
488
+ </script>
489
+
490
+ <svelte:window
491
+ on:click={handle_click_outside}
492
+ on:touchstart={handle_click_outside}
493
+ on:resize={() => set_cell_widths()}
494
+ />
495
+
496
+ <div class:label={label && label.length !== 0} use:copy>
497
+ {#if label && label.length !== 0 && show_label}
498
+ <p>
499
+ {label}
500
+ </p>
501
+ {/if}
502
+ <div
503
+ bind:this={parent}
504
+ class="table-wrap"
505
+ class:dragging
506
+ class:no-wrap={!wrap}
507
+ style="height:{table_height}px"
508
+ on:keydown={(e) => handle_keydown(e)}
509
+ role="grid"
510
+ tabindex="0"
511
+ >
512
+ <table
513
+ bind:contentRect={t_rect}
514
+ bind:this={table}
515
+ class:fixed-layout={column_widths.length != 0}
516
+ >
517
+ {#if label && label.length !== 0}
518
+ <caption class="sr-only">{label}</caption>
519
+ {/if}
520
+ <thead>
521
+ <tr>
522
+ {#each _headers as { value, id }, i (id)}
523
+ <th
524
+ class:editing={header_edit === i}
525
+ aria-sort={get_sort_status(value, sort_by, sort_direction)}
526
+ style:width={column_widths.length ? column_widths[i] : undefined}
527
+ >
528
+ <div class="cell-wrap">
529
+ <EditableCell
530
+ {value}
531
+ {latex_delimiters}
532
+ {line_breaks}
533
+ header
534
+ edit={false}
535
+ el={null}
536
+ {root}
537
+ />
538
+
539
+ <div
540
+ class:sorted={sort_by === i}
541
+ class:des={sort_by === i && sort_direction === "des"}
542
+ class="sort-button {sort_direction} "
543
+ >
544
+ <svg
545
+ width="1em"
546
+ height="1em"
547
+ viewBox="0 0 9 7"
548
+ fill="none"
549
+ xmlns="http://www.w3.org/2000/svg"
550
+ >
551
+ <path d="M4.49999 0L8.3971 6.75H0.602875L4.49999 0Z" />
552
+ </svg>
553
+ </div>
554
+ </div>
555
+ </th>
556
+ {/each}
557
+ </tr>
558
+ </thead>
559
+ <tbody>
560
+ <tr>
561
+ {#each max as { value, id }, j (id)}
562
+ <td tabindex="-1" bind:this={cells[j]}>
563
+ <div class="cell-wrap">
564
+ <EditableCell
565
+ {value}
566
+ {latex_delimiters}
567
+ {line_breaks}
568
+ datatype={Array.isArray(datatype) ? datatype[j] : datatype}
569
+ edit={false}
570
+ el={null}
571
+ {root}
572
+ />
573
+ </div>
574
+ </td>
575
+ {/each}
576
+ </tr>
577
+ </tbody>
578
+ </table>
579
+ <Upload
580
+ {upload}
581
+ {stream_handler}
582
+ flex={false}
583
+ center={false}
584
+ boundedheight={false}
585
+ disable_click={true}
586
+ {root}
587
+ on:load={(e) => blob_to_string(data_uri_to_blob(e.detail.data))}
588
+ bind:dragging
589
+ >
590
+ <VirtualTable
591
+ bind:items={data}
592
+ max_height={height}
593
+ bind:actual_height={table_height}
594
+ bind:table_scrollbar_width={scrollbar_width}
595
+ selected={selected_index}
596
+ >
597
+ {#if label && label.length !== 0}
598
+ <caption class="sr-only">{label}</caption>
599
+ {/if}
600
+ <tr slot="thead">
601
+ {#each _headers as { value, id }, i (id)}
602
+ <th
603
+ class:focus={header_edit === i || selected_header === i}
604
+ aria-sort={get_sort_status(value, sort_by, sort_direction)}
605
+ style="width: var(--cell-width-{i});"
606
+ >
607
+ <div class="cell-wrap">
608
+ <EditableCell
609
+ bind:value={_headers[i].value}
610
+ bind:el={els[id].input}
611
+ {latex_delimiters}
612
+ {line_breaks}
613
+ edit={header_edit === i}
614
+ on:keydown={end_header_edit}
615
+ on:dblclick={() => edit_header(i)}
616
+ {select_on_focus}
617
+ header
618
+ {root}
619
+ />
620
+
621
+ <!-- TODO: fix -->
622
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
623
+ <!-- svelte-ignore a11y-no-static-element-interactions-->
624
+ <div
625
+ class:sorted={sort_by === i}
626
+ class:des={sort_by === i && sort_direction === "des"}
627
+ class="sort-button {sort_direction} "
628
+ on:click={() => handle_sort(i)}
629
+ >
630
+ <svg
631
+ width="1em"
632
+ height="1em"
633
+ viewBox="0 0 9 7"
634
+ fill="none"
635
+ xmlns="http://www.w3.org/2000/svg"
636
+ >
637
+ <path d="M4.49999 0L8.3971 6.75H0.602875L4.49999 0Z" />
638
+ </svg>
639
+ </div>
640
+ </div>
641
+ </th>
642
+ {/each}
643
+ </tr>
644
+
645
+ <tr slot="tbody" let:item let:index class:row_odd={index % 2 === 0}>
646
+ {#each item as { value, id }, j (id)}
647
+ <td
648
+ tabindex="0"
649
+ on:touchstart={() => start_edit(index, j)}
650
+ on:click={() => handle_cell_click(index, j)}
651
+ on:dblclick={() => start_edit(index, j)}
652
+ style:width="var(--cell-width-{j})"
653
+ style={styling?.[index]?.[j] || ""}
654
+ class:focus={dequal(selected, [index, j])}
655
+ >
656
+ <div class="cell-wrap">
657
+ <EditableCell
658
+ bind:value={data[index][j].value}
659
+ bind:el={els[id].input}
660
+ display_value={display_value?.[index]?.[j]}
661
+ {latex_delimiters}
662
+ {line_breaks}
663
+ {editable}
664
+ edit={dequal(editing, [index, j])}
665
+ datatype={Array.isArray(datatype) ? datatype[j] : datatype}
666
+ on:blur={() => ((clear_on_focus = false), parent.focus())}
667
+ {clear_on_focus}
668
+ {root}
669
+ />
670
+ </div>
671
+ </td>
672
+ {/each}
673
+ </tr>
674
+ </VirtualTable>
675
+ </Upload>
676
+ </div>
677
+ {#if editable}
678
+ <div class="controls-wrap">
679
+ {#if row_count[1] === "dynamic"}
680
+ <span class="button-wrap">
681
+ <BaseButton
682
+ variant="secondary"
683
+ size="sm"
684
+ on:click={(e) => (e.stopPropagation(), add_row())}
685
+ >
686
+ <svg
687
+ xmlns="http://www.w3.org/2000/svg"
688
+ xmlns:xlink="http://www.w3.org/1999/xlink"
689
+ aria-hidden="true"
690
+ role="img"
691
+ width="1em"
692
+ height="1em"
693
+ preserveAspectRatio="xMidYMid meet"
694
+ viewBox="0 0 32 32"
695
+ >
696
+ <path
697
+ fill="currentColor"
698
+ d="M24.59 16.59L17 24.17V4h-2v20.17l-7.59-7.58L6 18l10 10l10-10l-1.41-1.41z"
699
+ />
700
+ </svg>
701
+ {i18n("dataframe.new_row")}
702
+ </BaseButton>
703
+ </span>
704
+ {/if}
705
+ {#if col_count[1] === "dynamic"}
706
+ <span class="button-wrap">
707
+ <BaseButton
708
+ variant="secondary"
709
+ size="sm"
710
+ on:click={(e) => (e.stopPropagation(), add_col())}
711
+ >
712
+ <svg
713
+ xmlns="http://www.w3.org/2000/svg"
714
+ xmlns:xlink="http://www.w3.org/1999/xlink"
715
+ aria-hidden="true"
716
+ role="img"
717
+ width="1em"
718
+ height="1em"
719
+ preserveAspectRatio="xMidYMid meet"
720
+ viewBox="0 0 32 32"
721
+ >
722
+ <path
723
+ fill="currentColor"
724
+ d="m18 6l-1.43 1.393L24.15 15H4v2h20.15l-7.58 7.573L18 26l10-10L18 6z"
725
+ />
726
+ </svg>
727
+ {i18n("dataframe.new_column")}
728
+ </BaseButton>
729
+ </span>
730
+ {/if}
731
+ </div>
732
+ {/if}
733
+ </div>
734
+
735
+ <style>
736
+ .button-wrap:hover svg {
737
+ color: var(--color-accent);
738
+ }
739
+
740
+ .button-wrap svg {
741
+ margin-right: var(--size-1);
742
+ margin-left: -5px;
743
+ }
744
+
745
+ .label p {
746
+ position: relative;
747
+ z-index: var(--layer-4);
748
+ margin-bottom: var(--size-2);
749
+ color: var(--block-label-text-color);
750
+ font-size: var(--block-label-text-size);
751
+ }
752
+
753
+ .table-wrap {
754
+ position: relative;
755
+ transition: 150ms;
756
+ border: 1px solid var(--border-color-primary);
757
+ border-radius: var(--table-radius);
758
+ overflow: hidden;
759
+ }
760
+
761
+ .table-wrap:focus-within {
762
+ outline: none;
763
+ background-color: none;
764
+ }
765
+
766
+ .dragging {
767
+ border-color: var(--color-accent);
768
+ }
769
+
770
+ .no-wrap {
771
+ white-space: nowrap;
772
+ }
773
+
774
+ table {
775
+ position: absolute;
776
+ opacity: 0;
777
+ transition: 150ms;
778
+ width: var(--size-full);
779
+ table-layout: auto;
780
+ color: var(--body-text-color);
781
+ font-size: var(--input-text-size);
782
+ line-height: var(--line-md);
783
+ font-family: var(--font-mono);
784
+ border-spacing: 0;
785
+ }
786
+
787
+ div:not(.no-wrap) td {
788
+ overflow-wrap: anywhere;
789
+ }
790
+
791
+ div.no-wrap td {
792
+ overflow-x: hidden;
793
+ }
794
+
795
+ table.fixed-layout {
796
+ table-layout: fixed;
797
+ }
798
+
799
+ thead {
800
+ position: sticky;
801
+ top: 0;
802
+ left: 0;
803
+ z-index: var(--layer-1);
804
+ box-shadow: var(--shadow-drop);
805
+ }
806
+
807
+ tr {
808
+ border-bottom: 1px solid var(--border-color-primary);
809
+ text-align: left;
810
+ }
811
+
812
+ tr > * + * {
813
+ border-right-width: 0px;
814
+ border-left-width: 1px;
815
+ border-style: solid;
816
+ border-color: var(--border-color-primary);
817
+ }
818
+
819
+ th,
820
+ td {
821
+ --ring-color: transparent;
822
+ position: relative;
823
+ outline: none;
824
+ box-shadow: inset 0 0 0 1px var(--ring-color);
825
+ padding: 0;
826
+ }
827
+
828
+ th:first-child {
829
+ border-top-left-radius: var(--table-radius);
830
+ }
831
+
832
+ th:last-child {
833
+ border-top-right-radius: var(--table-radius);
834
+ }
835
+
836
+ th.focus,
837
+ td.focus {
838
+ --ring-color: var(--color-accent);
839
+ }
840
+
841
+ tr:last-child td:first-child {
842
+ border-bottom-left-radius: var(--table-radius);
843
+ }
844
+
845
+ tr:last-child td:last-child {
846
+ border-bottom-right-radius: var(--table-radius);
847
+ }
848
+
849
+ tr th {
850
+ background: var(--table-even-background-fill);
851
+ }
852
+
853
+ th svg {
854
+ fill: currentColor;
855
+ font-size: 10px;
856
+ }
857
+
858
+ .sort-button {
859
+ display: flex;
860
+ flex: none;
861
+ justify-content: center;
862
+ align-items: center;
863
+ transition: 150ms;
864
+ cursor: pointer;
865
+ padding: var(--size-2);
866
+ color: var(--body-text-color-subdued);
867
+ line-height: var(--text-sm);
868
+ }
869
+
870
+ .sort-button:hover {
871
+ color: var(--body-text-color);
872
+ }
873
+
874
+ .des {
875
+ transform: scaleY(-1);
876
+ }
877
+
878
+ .sort-button.sorted {
879
+ color: var(--color-accent);
880
+ }
881
+
882
+ .editing {
883
+ background: var(--table-editing);
884
+ }
885
+
886
+ .cell-wrap {
887
+ display: flex;
888
+ align-items: center;
889
+ outline: none;
890
+ height: var(--size-full);
891
+ min-height: var(--size-9);
892
+ }
893
+
894
+ .controls-wrap {
895
+ display: flex;
896
+ justify-content: flex-end;
897
+ padding-top: var(--size-2);
898
+ }
899
+
900
+ .controls-wrap > * + * {
901
+ margin-left: var(--size-1);
902
+ }
903
+
904
+ .row_odd {
905
+ background: var(--table-odd-background-fill);
906
+ }
907
+
908
+ .row_odd.focus {
909
+ background: var(--background-fill-primary);
910
+ }
911
+
912
+ table {
913
+ border-collapse: separate;
914
+ }
915
+ </style>