@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.
@@ -1,5 +1,4 @@
1
1
  <script>import { afterUpdate, createEventDispatcher, tick, onMount } from "svelte";
2
- import { dsvFormat } from "d3-dsv";
3
2
  import { dequal } from "dequal/lite";
4
3
  import { Upload } from "@gradio/upload";
5
4
  import EditableCell from "./EditableCell.svelte";
@@ -7,7 +6,18 @@ import {} from "@gradio/client";
7
6
  import VirtualTable from "./VirtualTable.svelte";
8
7
  import CellMenu from "./CellMenu.svelte";
9
8
  import Toolbar from "./Toolbar.svelte";
10
- import { copy_table_data } from "./table_utils";
9
+ import {
10
+ is_cell_selected,
11
+ handle_selection,
12
+ handle_delete_key,
13
+ should_show_cell_menu,
14
+ get_next_cell_coordinates,
15
+ get_range_selection,
16
+ move_cursor,
17
+ get_current_indices,
18
+ handle_click_outside as handle_click_outside_util
19
+ } from "./selection_utils";
20
+ import { copy_table_data, get_max, handle_file_upload } from "./table_utils";
11
21
  export let datatype;
12
22
  export let label = null;
13
23
  export let show_label = true;
@@ -29,30 +39,28 @@ export let stream_handler;
29
39
  export let show_fullscreen_button = false;
30
40
  export let show_copy_button = false;
31
41
  export let value_is_output = false;
42
+ export let max_chars = void 0;
43
+ let selected_cells = [];
44
+ $:
45
+ selected_cells = [...selected_cells];
32
46
  let selected = false;
33
- let clicked_cell = void 0;
47
+ $:
48
+ selected = selected_cells.length > 0 ? selected_cells[selected_cells.length - 1] : false;
34
49
  export let display_value = null;
35
50
  export let styling = null;
36
51
  let t_rect;
52
+ let els = {};
53
+ let data_binding = {};
37
54
  const dispatch = createEventDispatcher();
38
55
  let editing = false;
56
+ let clear_on_focus = false;
57
+ let header_edit = false;
58
+ let selected_header = false;
59
+ let active_cell_menu = null;
60
+ let active_header_menu = null;
61
+ let is_fullscreen = false;
62
+ let dragging = false;
39
63
  const get_data_at = (row, col) => data?.[row]?.[col]?.value;
40
- let last_selected = null;
41
- $: {
42
- if (selected !== false && !dequal(selected, last_selected)) {
43
- const [row, col] = selected;
44
- if (!isNaN(row) && !isNaN(col) && data[row]) {
45
- dispatch("select", {
46
- index: [row, col],
47
- value: get_data_at(row, col),
48
- row_value: data[row].map((d) => d.value)
49
- });
50
- last_selected = selected;
51
- }
52
- }
53
- }
54
- let els = {};
55
- let data_binding = {};
56
64
  function make_id() {
57
65
  return Math.random().toString(36).substring(2, 15);
58
66
  }
@@ -135,36 +143,6 @@ function get_sort_status(name, _sort, direction) {
135
143
  }
136
144
  return "none";
137
145
  }
138
- function get_current_indices(id) {
139
- return data.reduce(
140
- (acc, arr, i) => {
141
- const j = arr.reduce(
142
- (_acc, _data, k) => id === _data.id ? k : _acc,
143
- -1
144
- );
145
- return j === -1 ? acc : [i, j];
146
- },
147
- [-1, -1]
148
- );
149
- }
150
- function move_cursor(key, current_coords) {
151
- const dir = {
152
- ArrowRight: [0, 1],
153
- ArrowLeft: [0, -1],
154
- ArrowDown: [1, 0],
155
- ArrowUp: [-1, 0]
156
- }[key];
157
- const i = current_coords[0] + dir[0];
158
- const j = current_coords[1] + dir[1];
159
- if (i < 0 && j <= 0) {
160
- selected_header = j;
161
- selected = false;
162
- } else {
163
- const is_data = data[i]?.[j];
164
- selected = is_data ? [i, j] : selected;
165
- }
166
- }
167
- let clear_on_focus = false;
168
146
  async function handle_keydown(event) {
169
147
  if (selected_header !== false && header_edit === false) {
170
148
  switch (event.key) {
@@ -187,6 +165,43 @@ async function handle_keydown(event) {
187
165
  break;
188
166
  }
189
167
  }
168
+ if (event.key === "Delete" || event.key === "Backspace") {
169
+ if (!editable)
170
+ return;
171
+ if (editing) {
172
+ const [row, col] = editing;
173
+ const input_el = els[data[row][col].id].input;
174
+ if (input_el && input_el.selectionStart !== input_el.selectionEnd) {
175
+ return;
176
+ }
177
+ if (event.key === "Delete" && input_el?.selectionStart !== input_el?.value.length) {
178
+ return;
179
+ }
180
+ if (event.key === "Backspace" && input_el?.selectionStart !== 0) {
181
+ return;
182
+ }
183
+ }
184
+ event.preventDefault();
185
+ if (selected_cells.length > 0) {
186
+ data = handle_delete_key(data, selected_cells);
187
+ dispatch("change", {
188
+ data: data.map((row) => row.map((cell) => cell.value)),
189
+ headers: _headers.map((h) => h.value),
190
+ metadata: null
191
+ });
192
+ if (!value_is_output) {
193
+ dispatch("input");
194
+ }
195
+ }
196
+ return;
197
+ }
198
+ if (event.key === "c" && (event.metaKey || event.ctrlKey)) {
199
+ event.preventDefault();
200
+ if (selected_cells.length > 0) {
201
+ await handle_copy();
202
+ }
203
+ return;
204
+ }
190
205
  if (!selected) {
191
206
  return;
192
207
  }
@@ -199,7 +214,25 @@ async function handle_keydown(event) {
199
214
  if (editing)
200
215
  break;
201
216
  event.preventDefault();
202
- move_cursor(event.key, [i, j]);
217
+ const next_coords = move_cursor(event.key, [i, j], data);
218
+ if (next_coords) {
219
+ if (event.shiftKey) {
220
+ selected_cells = get_range_selection(
221
+ selected_cells.length > 0 ? selected_cells[0] : [i, j],
222
+ next_coords
223
+ );
224
+ editing = false;
225
+ } else {
226
+ selected_cells = [next_coords];
227
+ editing = next_coords;
228
+ clear_on_focus = false;
229
+ }
230
+ selected = next_coords;
231
+ } else if (next_coords === false && event.key === "ArrowUp" && i === 0) {
232
+ selected_header = j;
233
+ selected = false;
234
+ editing = false;
235
+ }
203
236
  break;
204
237
  case "Escape":
205
238
  if (!editable)
@@ -227,34 +260,26 @@ async function handle_keydown(event) {
227
260
  selected = [i, j];
228
261
  } else {
229
262
  editing = [i, j];
263
+ clear_on_focus = false;
230
264
  }
231
265
  }
232
266
  break;
233
- case "Backspace":
234
- if (!editable)
235
- break;
236
- if (!editing) {
237
- event.preventDefault();
238
- data[i][j].value = "";
239
- }
240
- break;
241
- case "Delete":
242
- if (!editable)
243
- break;
244
- if (!editing) {
245
- event.preventDefault();
246
- data[i][j].value = "";
247
- }
248
- break;
249
267
  case "Tab":
250
- let direction = event.shiftKey ? -1 : 1;
251
- let is_data_x = data[i][j + direction];
252
- let is_data_y = data?.[i + direction]?.[direction > 0 ? 0 : _headers.length - 1];
253
- if (is_data_x || is_data_y) {
254
- event.preventDefault();
255
- selected = is_data_x ? [i, j + direction] : [i + direction, direction > 0 ? 0 : _headers.length - 1];
256
- }
268
+ event.preventDefault();
257
269
  editing = false;
270
+ const next_cell = get_next_cell_coordinates(
271
+ [i, j],
272
+ data,
273
+ event.shiftKey
274
+ );
275
+ if (next_cell) {
276
+ selected_cells = [next_cell];
277
+ selected = next_cell;
278
+ if (editable) {
279
+ editing = next_cell;
280
+ clear_on_focus = false;
281
+ }
282
+ }
258
283
  break;
259
284
  default:
260
285
  if (!editable)
@@ -279,16 +304,13 @@ function handle_sort(col) {
279
304
  }
280
305
  }
281
306
  }
282
- let header_edit;
283
- let select_on_focus = false;
284
- let selected_header = false;
285
307
  async function edit_header(i, _select = false) {
286
308
  if (!editable || col_count[1] !== "dynamic" || header_edit === i)
287
309
  return;
288
310
  selected = false;
311
+ selected_cells = [];
289
312
  selected_header = i;
290
313
  header_edit = i;
291
- select_on_focus = _select;
292
314
  }
293
315
  function end_header_edit(event) {
294
316
  if (!editable)
@@ -349,78 +371,14 @@ async function add_col(index) {
349
371
  });
350
372
  }
351
373
  function handle_click_outside(event) {
352
- if (active_cell_menu && !event.target.closest(".cell-menu") || active_header_menu && !event.target.closest(".cell-menu")) {
374
+ if (handle_click_outside_util(event, parent)) {
375
+ editing = false;
376
+ selected_cells = [];
377
+ header_edit = false;
378
+ selected_header = false;
353
379
  active_cell_menu = null;
354
380
  active_header_menu = null;
355
381
  }
356
- const [trigger] = event.composedPath();
357
- if (parent.contains(trigger)) {
358
- return;
359
- }
360
- clicked_cell = void 0;
361
- editing = false;
362
- selected = false;
363
- header_edit = false;
364
- selected_header = false;
365
- active_cell_menu = null;
366
- active_header_menu = null;
367
- }
368
- function guess_delimitaor(text, possibleDelimiters) {
369
- return possibleDelimiters.filter(weedOut);
370
- function weedOut(delimiter) {
371
- var cache = -1;
372
- return text.split("\n").every(checkLength);
373
- function checkLength(line) {
374
- if (!line) {
375
- return true;
376
- }
377
- var length = line.split(delimiter).length;
378
- if (cache < 0) {
379
- cache = length;
380
- }
381
- return cache === length && length > 1;
382
- }
383
- }
384
- }
385
- function data_uri_to_blob(data_uri) {
386
- const byte_str = atob(data_uri.split(",")[1]);
387
- const mime_str = data_uri.split(",")[0].split(":")[1].split(";")[0];
388
- const ab = new ArrayBuffer(byte_str.length);
389
- const ia = new Uint8Array(ab);
390
- for (let i = 0; i < byte_str.length; i++) {
391
- ia[i] = byte_str.charCodeAt(i);
392
- }
393
- return new Blob([ab], { type: mime_str });
394
- }
395
- function blob_to_string(blob) {
396
- const reader = new FileReader();
397
- function handle_read(e) {
398
- if (!e?.target?.result || typeof e.target.result !== "string")
399
- return;
400
- const [delimiter] = guess_delimitaor(e.target.result, [",", " "]);
401
- const [head, ...rest] = dsvFormat(delimiter).parseRows(e.target.result);
402
- _headers = make_headers(
403
- col_count[1] === "fixed" ? head.slice(0, col_count[0]) : head
404
- );
405
- values = rest;
406
- reader.removeEventListener("loadend", handle_read);
407
- }
408
- reader.addEventListener("loadend", handle_read);
409
- reader.readAsText(blob);
410
- }
411
- let dragging = false;
412
- function get_max(_d) {
413
- if (!_d || _d.length === 0 || !_d[0])
414
- return [];
415
- let max2 = _d[0].slice();
416
- for (let i = 0; i < _d.length; i++) {
417
- for (let j = 0; j < _d[i].length; j++) {
418
- if (`${max2[j].value}`.length < `${_d[i][j].value}`.length) {
419
- max2[j] = _d[i][j];
420
- }
421
- }
422
- }
423
- return max2;
424
382
  }
425
383
  $:
426
384
  max = get_max(data);
@@ -476,7 +434,7 @@ function sort_data(_data, _display_value, _styling, col, dir) {
476
434
  });
477
435
  data = data;
478
436
  if (id) {
479
- const [i, j] = get_current_indices(id);
437
+ const [i, j] = get_current_indices(id, data);
480
438
  selected = [i, j];
481
439
  }
482
440
  }
@@ -486,7 +444,7 @@ $:
486
444
  selected_index = !!selected && selected[0];
487
445
  let is_visible = false;
488
446
  onMount(() => {
489
- const observer = new IntersectionObserver((entries, observer2) => {
447
+ const observer = new IntersectionObserver((entries) => {
490
448
  entries.forEach((entry) => {
491
449
  if (entry.isIntersecting && !is_visible) {
492
450
  set_cell_widths();
@@ -509,8 +467,34 @@ onMount(() => {
509
467
  );
510
468
  };
511
469
  });
512
- let highlighted_column = null;
513
- let active_cell_menu = null;
470
+ function handle_cell_click(event, row, col) {
471
+ event.preventDefault();
472
+ event.stopPropagation();
473
+ clear_on_focus = false;
474
+ active_cell_menu = null;
475
+ active_header_menu = null;
476
+ selected_header = false;
477
+ header_edit = false;
478
+ selected_cells = handle_selection([row, col], selected_cells, event);
479
+ if (selected_cells.length === 1 && editable) {
480
+ editing = [row, col];
481
+ tick().then(() => {
482
+ const input_el = els[data[row][col].id].input;
483
+ if (input_el) {
484
+ input_el.focus();
485
+ input_el.selectionStart = input_el.selectionEnd = input_el.value.length;
486
+ }
487
+ });
488
+ } else {
489
+ editing = false;
490
+ }
491
+ toggle_cell_button(row, col);
492
+ dispatch("select", {
493
+ index: [row, col],
494
+ value: get_data_at(row, col),
495
+ row_value: data[row].map((d) => d.value)
496
+ });
497
+ }
514
498
  function toggle_cell_menu(event, row, col) {
515
499
  event.stopPropagation();
516
500
  if (active_cell_menu && active_cell_menu.row === row && active_cell_menu.col === col) {
@@ -519,12 +503,7 @@ function toggle_cell_menu(event, row, col) {
519
503
  const cell = event.target.closest("td");
520
504
  if (cell) {
521
505
  const rect = cell.getBoundingClientRect();
522
- active_cell_menu = {
523
- row,
524
- col,
525
- x: rect.right,
526
- y: rect.bottom
527
- };
506
+ active_cell_menu = { row, col, x: rect.right, y: rect.bottom };
528
507
  }
529
508
  }
530
509
  }
@@ -547,21 +526,11 @@ function handle_resize() {
547
526
  }
548
527
  let active_button = null;
549
528
  function toggle_header_button(col) {
550
- if (active_button?.type === "header" && active_button.col === col) {
551
- active_button = null;
552
- } else {
553
- active_button = { type: "header", col };
554
- }
529
+ active_button = active_button?.type === "header" && active_button.col === col ? null : { type: "header", col };
555
530
  }
556
531
  function toggle_cell_button(row, col) {
557
- if (active_button?.type === "cell" && active_button.row === row && active_button.col === col) {
558
- active_button = null;
559
- } else {
560
- active_button = { type: "cell", row, col };
561
- }
532
+ active_button = active_button?.type === "cell" && active_button.row === row && active_button.col === col ? null : { type: "cell", row, col };
562
533
  }
563
- let active_header_menu = null;
564
- let is_fullscreen = false;
565
534
  function toggle_fullscreen() {
566
535
  if (!document.fullscreenElement) {
567
536
  parent.requestFullscreen();
@@ -575,7 +544,7 @@ function handle_fullscreen_change() {
575
544
  is_fullscreen = !!document.fullscreenElement;
576
545
  }
577
546
  async function handle_copy() {
578
- await copy_table_data(data, _headers);
547
+ await copy_table_data(data, _headers, selected_cells);
579
548
  }
580
549
  function toggle_header_menu(event, col) {
581
550
  event.stopPropagation();
@@ -585,11 +554,7 @@ function toggle_header_menu(event, col) {
585
554
  const header = event.target.closest("th");
586
555
  if (header) {
587
556
  const rect = header.getBoundingClientRect();
588
- active_header_menu = {
589
- col,
590
- x: rect.right,
591
- y: rect.bottom
592
- };
557
+ active_header_menu = { col, x: rect.right, y: rect.bottom };
593
558
  }
594
559
  }
595
560
  }
@@ -687,6 +652,7 @@ function delete_col_at(index) {
687
652
  edit={false}
688
653
  el={null}
689
654
  {root}
655
+ {editable}
690
656
  />
691
657
 
692
658
  <div
@@ -722,6 +688,7 @@ function delete_col_at(index) {
722
688
  edit={false}
723
689
  el={null}
724
690
  {root}
691
+ {editable}
725
692
  />
726
693
  </div>
727
694
  </td>
@@ -737,8 +704,20 @@ function delete_col_at(index) {
737
704
  boundedheight={false}
738
705
  disable_click={true}
739
706
  {root}
740
- on:load={(e) => blob_to_string(data_uri_to_blob(e.detail.data))}
707
+ on:load={({ detail }) =>
708
+ handle_file_upload(
709
+ detail.data,
710
+ col_count,
711
+ (head) => {
712
+ _headers = make_headers(head);
713
+ return _headers;
714
+ },
715
+ (vals) => {
716
+ values = vals;
717
+ }
718
+ )}
741
719
  bind:dragging
720
+ aria_label={i18n("dataframe.drop_to_upload")}
742
721
  >
743
722
  <VirtualTable
744
723
  bind:items={data}
@@ -766,6 +745,7 @@ function delete_col_at(index) {
766
745
  <div class="cell-wrap">
767
746
  <div class="header-content">
768
747
  <EditableCell
748
+ {max_chars}
769
749
  bind:value={_headers[i].value}
770
750
  bind:el={els[id].input}
771
751
  {latex_delimiters}
@@ -775,6 +755,7 @@ function delete_col_at(index) {
775
755
  on:dblclick={() => edit_header(i)}
776
756
  header
777
757
  {root}
758
+ {editable}
778
759
  />
779
760
  <button
780
761
  class:sorted={sort_by === i}
@@ -803,7 +784,7 @@ function delete_col_at(index) {
803
784
  class="cell-menu-button"
804
785
  on:click={(event) => toggle_header_menu(event, i)}
805
786
  >
806
-
787
+ &#8942;
807
788
  </button>
808
789
  {/if}
809
790
  </div>
@@ -819,40 +800,24 @@ function delete_col_at(index) {
819
800
  <td
820
801
  tabindex="0"
821
802
  on:touchstart={(event) => {
822
- event.preventDefault();
823
- event.stopPropagation();
824
- clear_on_focus = false;
825
- clicked_cell = { row: index, col: j };
826
- selected = [index, j];
827
- selected_header = false;
828
- header_edit = false;
829
- if (editable) {
830
- editing = [index, j];
831
- }
832
- toggle_cell_button(index, j);
803
+ const touch = event.touches[0];
804
+ const mouseEvent = new MouseEvent("click", {
805
+ clientX: touch.clientX,
806
+ clientY: touch.clientY,
807
+ bubbles: true,
808
+ cancelable: true,
809
+ view: window
810
+ });
811
+ handle_cell_click(mouseEvent, index, j);
833
812
  }}
834
813
  on:mousedown={(event) => {
835
814
  event.preventDefault();
836
815
  event.stopPropagation();
837
816
  }}
838
- on:click={(event) => {
839
- event.preventDefault();
840
- event.stopPropagation();
841
- clear_on_focus = false;
842
- active_cell_menu = null;
843
- active_header_menu = null;
844
- clicked_cell = { row: index, col: j };
845
- selected = [index, j];
846
- selected_header = false;
847
- header_edit = false;
848
- if (editable) {
849
- editing = [index, j];
850
- }
851
- toggle_cell_button(index, j);
852
- }}
817
+ on:click={(event) => handle_cell_click(event, index, j)}
853
818
  style:width="var(--cell-width-{j})"
854
819
  style={styling?.[index]?.[j] || ""}
855
- class:focus={dequal(selected, [index, j])}
820
+ class={is_cell_selected([index, j], selected_cells)}
856
821
  class:menu-active={active_cell_menu &&
857
822
  active_cell_menu.row === index &&
858
823
  active_cell_menu.col === j}
@@ -871,15 +836,25 @@ function delete_col_at(index) {
871
836
  clear_on_focus = false;
872
837
  parent.focus();
873
838
  }}
839
+ on:focus={() => {
840
+ const row = index;
841
+ const col = j;
842
+ if (
843
+ !selected_cells.some(([r, c]) => r === row && c === col)
844
+ ) {
845
+ selected_cells = [[row, col]];
846
+ }
847
+ }}
874
848
  {clear_on_focus}
875
849
  {root}
850
+ {max_chars}
876
851
  />
877
- {#if editable}
852
+ {#if editable && should_show_cell_menu([index, j], selected_cells, editable)}
878
853
  <button
879
854
  class="cell-menu-button"
880
855
  on:click={(event) => toggle_cell_menu(event, index, j)}
881
856
  >
882
-
857
+ &#8942;
883
858
  </button>
884
859
  {/if}
885
860
  </div>
@@ -931,21 +906,10 @@ function delete_col_at(index) {
931
906
  {/if}
932
907
 
933
908
  <style>
934
- .button-wrap:hover svg {
935
- color: var(--color-accent);
936
- }
937
-
938
- .button-wrap svg {
939
- margin-right: var(--size-1);
940
- margin-left: -5px;
941
- }
942
-
943
- .label p {
944
- position: relative;
945
- z-index: var(--layer-4);
946
- margin-bottom: var(--size-2);
947
- color: var(--block-label-text-color);
948
- font-size: var(--block-label-text-size);
909
+ .table-container {
910
+ display: flex;
911
+ flex-direction: column;
912
+ gap: var(--size-2);
949
913
  }
950
914
 
951
915
  .table-wrap {
@@ -958,7 +922,6 @@ function delete_col_at(index) {
958
922
 
959
923
  .table-wrap:focus-within {
960
924
  outline: none;
961
- background-color: none;
962
925
  }
963
926
 
964
927
  .dragging {
@@ -980,6 +943,7 @@ function delete_col_at(index) {
980
943
  line-height: var(--line-md);
981
944
  font-family: var(--font-mono);
982
945
  border-spacing: 0;
946
+ border-collapse: separate;
983
947
  }
984
948
 
985
949
  div:not(.no-wrap) td {
@@ -1034,6 +998,12 @@ function delete_col_at(index) {
1034
998
  th.focus,
1035
999
  td.focus {
1036
1000
  --ring-color: var(--color-accent);
1001
+ box-shadow: inset 0 0 0 2px var(--ring-color);
1002
+ z-index: var(--layer-1);
1003
+ }
1004
+
1005
+ th.focus {
1006
+ z-index: var(--layer-2);
1037
1007
  }
1038
1008
 
1039
1009
  tr:last-child td:first-child {
@@ -1062,7 +1032,6 @@ function delete_col_at(index) {
1062
1032
  cursor: pointer;
1063
1033
  padding: var(--size-2);
1064
1034
  color: var(--body-text-color-subdued);
1065
- line-height: var(--text-sm);
1066
1035
  }
1067
1036
 
1068
1037
  .sort-button:hover {
@@ -1083,11 +1052,11 @@ function delete_col_at(index) {
1083
1052
 
1084
1053
  .cell-wrap {
1085
1054
  display: flex;
1086
- align-items: center;
1055
+ align-items: flex-start;
1087
1056
  outline: none;
1088
- height: var(--size-full);
1089
1057
  min-height: var(--size-9);
1090
- overflow: hidden;
1058
+ position: relative;
1059
+ height: auto;
1091
1060
  }
1092
1061
 
1093
1062
  .header-content {
@@ -1096,12 +1065,9 @@ function delete_col_at(index) {
1096
1065
  overflow: hidden;
1097
1066
  flex-grow: 1;
1098
1067
  min-width: 0;
1099
- }
1100
-
1101
- .controls-wrap {
1102
- display: flex;
1103
- justify-content: flex-end;
1104
- padding-top: var(--size-2);
1068
+ white-space: normal;
1069
+ overflow-wrap: break-word;
1070
+ word-break: break-word;
1105
1071
  }
1106
1072
 
1107
1073
  .row_odd {
@@ -1112,10 +1078,6 @@ function delete_col_at(index) {
1112
1078
  background: var(--background-fill-primary);
1113
1079
  }
1114
1080
 
1115
- table {
1116
- border-collapse: separate;
1117
- }
1118
-
1119
1081
  .cell-menu-button {
1120
1082
  flex-shrink: 0;
1121
1083
  display: none;
@@ -1128,28 +1090,35 @@ function delete_col_at(index) {
1128
1090
  padding: 0;
1129
1091
  margin-right: var(--spacing-sm);
1130
1092
  z-index: var(--layer-1);
1093
+ position: absolute;
1094
+ right: var(--size-1);
1095
+ top: 50%;
1096
+ transform: translateY(-50%);
1131
1097
  }
1132
1098
 
1133
- .cell-menu-button:hover {
1134
- background-color: var(--color-bg-hover);
1099
+ .cell-selected .cell-menu-button {
1100
+ display: flex;
1101
+ align-items: center;
1102
+ justify-content: center;
1135
1103
  }
1136
1104
 
1137
- td.focus .cell-menu-button {
1105
+ .header-row {
1138
1106
  display: flex;
1107
+ justify-content: space-between;
1139
1108
  align-items: center;
1140
- justify-content: center;
1109
+ gap: var(--size-2);
1110
+ height: var(--size-6);
1111
+ min-height: var(--size-6);
1141
1112
  }
1142
1113
 
1143
- th .header-content {
1144
- white-space: normal;
1145
- overflow-wrap: break-word;
1146
- word-break: break-word;
1114
+ .label {
1115
+ flex: 1;
1147
1116
  }
1148
1117
 
1149
- .table-container {
1150
- display: flex;
1151
- flex-direction: column;
1152
- gap: var(--size-2);
1118
+ .label p {
1119
+ margin: 0;
1120
+ color: var(--block-label-text-color);
1121
+ font-size: var(--block-label-text-size);
1153
1122
  }
1154
1123
 
1155
1124
  .row-number,
@@ -1181,24 +1150,94 @@ function delete_col_at(index) {
1181
1150
  background: var(--table-odd-background-fill);
1182
1151
  }
1183
1152
 
1184
- .header-row {
1185
- display: flex;
1186
- justify-content: space-between;
1187
- align-items: center;
1188
- gap: var(--size-2);
1189
- height: var(--size-6);
1190
- min-height: var(--size-6);
1153
+ .cell-selected {
1154
+ --ring-color: var(--color-accent);
1155
+ box-shadow: inset 0 0 0 2px var(--ring-color);
1156
+ z-index: var(--layer-1);
1157
+ position: relative;
1191
1158
  }
1192
1159
 
1193
- .label {
1194
- flex: 1;
1160
+ .cell-selected.no-top {
1161
+ box-shadow:
1162
+ inset 2px 0 0 var(--ring-color),
1163
+ inset -2px 0 0 var(--ring-color),
1164
+ inset 0 -2px 0 var(--ring-color);
1195
1165
  }
1196
1166
 
1197
- .label p {
1198
- position: relative;
1199
- z-index: var(--layer-4);
1200
- margin: 0;
1201
- color: var(--block-label-text-color);
1202
- font-size: var(--block-label-text-size);
1167
+ .cell-selected.no-bottom {
1168
+ box-shadow:
1169
+ inset 2px 0 0 var(--ring-color),
1170
+ inset -2px 0 0 var(--ring-color),
1171
+ inset 0 2px 0 var(--ring-color);
1172
+ }
1173
+
1174
+ .cell-selected.no-left {
1175
+ box-shadow:
1176
+ inset 0 2px 0 var(--ring-color),
1177
+ inset -2px 0 0 var(--ring-color),
1178
+ inset 0 -2px 0 var(--ring-color);
1179
+ }
1180
+
1181
+ .cell-selected.no-right {
1182
+ box-shadow:
1183
+ inset 0 2px 0 var(--ring-color),
1184
+ inset 2px 0 0 var(--ring-color),
1185
+ inset 0 -2px 0 var(--ring-color);
1186
+ }
1187
+
1188
+ .cell-selected.no-top.no-left {
1189
+ box-shadow:
1190
+ inset -2px 0 0 var(--ring-color),
1191
+ inset 0 -2px 0 var(--ring-color);
1192
+ }
1193
+
1194
+ .cell-selected.no-top.no-right {
1195
+ box-shadow:
1196
+ inset 2px 0 0 var(--ring-color),
1197
+ inset 0 -2px 0 var(--ring-color);
1198
+ }
1199
+
1200
+ .cell-selected.no-bottom.no-left {
1201
+ box-shadow:
1202
+ inset -2px 0 0 var(--ring-color),
1203
+ inset 0 2px 0 var(--ring-color);
1204
+ }
1205
+
1206
+ .cell-selected.no-bottom.no-right {
1207
+ box-shadow:
1208
+ inset 2px 0 0 var(--ring-color),
1209
+ inset 0 2px 0 var(--ring-color);
1210
+ }
1211
+
1212
+ .cell-selected.no-top.no-bottom {
1213
+ box-shadow:
1214
+ inset 2px 0 0 var(--ring-color),
1215
+ inset -2px 0 0 var(--ring-color);
1216
+ }
1217
+
1218
+ .cell-selected.no-left.no-right {
1219
+ box-shadow:
1220
+ inset 0 2px 0 var(--ring-color),
1221
+ inset 0 -2px 0 var(--ring-color);
1222
+ }
1223
+
1224
+ .cell-selected.no-top.no-left.no-right {
1225
+ box-shadow: inset 0 -2px 0 var(--ring-color);
1226
+ }
1227
+
1228
+ .cell-selected.no-bottom.no-left.no-right {
1229
+ box-shadow: inset 0 2px 0 var(--ring-color);
1230
+ }
1231
+
1232
+ .cell-selected.no-left.no-top.no-bottom {
1233
+ box-shadow: inset -2px 0 0 var(--ring-color);
1234
+ }
1235
+
1236
+ .cell-selected.no-right.no-top.no-bottom {
1237
+ box-shadow: inset 2px 0 0 var(--ring-color);
1238
+ }
1239
+
1240
+ .cell-selected.no-top.no-bottom.no-left.no-right {
1241
+ box-shadow: none;
1203
1242
  }
1204
1243
  </style>