@gradio/dataframe 0.16.5 → 0.17.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/Dataframe.stories.svelte +202 -9
  3. package/Index.svelte +7 -13
  4. package/dist/Index.svelte +5 -9
  5. package/dist/Index.svelte.d.ts +9 -2
  6. package/dist/shared/CellMenu.svelte +91 -10
  7. package/dist/shared/CellMenu.svelte.d.ts +6 -0
  8. package/dist/shared/CellMenuButton.svelte +45 -0
  9. package/dist/shared/CellMenuButton.svelte.d.ts +16 -0
  10. package/dist/shared/CellMenuIcons.svelte +79 -0
  11. package/dist/shared/EditableCell.svelte +83 -14
  12. package/dist/shared/EditableCell.svelte.d.ts +12 -3
  13. package/dist/shared/EmptyRowButton.svelte +28 -0
  14. package/dist/shared/EmptyRowButton.svelte.d.ts +16 -0
  15. package/dist/shared/RowNumber.svelte +40 -0
  16. package/dist/shared/RowNumber.svelte.d.ts +17 -0
  17. package/dist/shared/Table.svelte +564 -1121
  18. package/dist/shared/Table.svelte.d.ts +4 -0
  19. package/dist/shared/TableCell.svelte +291 -0
  20. package/dist/shared/TableCell.svelte.d.ts +57 -0
  21. package/dist/shared/TableHeader.svelte +239 -0
  22. package/dist/shared/TableHeader.svelte.d.ts +45 -0
  23. package/dist/shared/Toolbar.svelte +18 -8
  24. package/dist/shared/VirtualTable.svelte +66 -19
  25. package/dist/shared/VirtualTable.svelte.d.ts +4 -0
  26. package/dist/shared/context/keyboard_context.d.ts +37 -0
  27. package/dist/shared/context/keyboard_context.js +12 -0
  28. package/dist/shared/context/selection_context.d.ts +32 -0
  29. package/dist/shared/context/selection_context.js +107 -0
  30. package/dist/shared/context/table_context.d.ts +141 -0
  31. package/dist/shared/context/table_context.js +375 -0
  32. package/dist/shared/icons/Padlock.svelte +24 -0
  33. package/dist/shared/icons/Padlock.svelte.d.ts +23 -0
  34. package/dist/shared/icons/SelectionButtons.svelte +85 -0
  35. package/dist/shared/icons/SelectionButtons.svelte.d.ts +18 -0
  36. package/dist/shared/icons/SortArrowDown.svelte +24 -0
  37. package/dist/shared/icons/SortArrowDown.svelte.d.ts +16 -0
  38. package/dist/shared/icons/SortArrowUp.svelte +24 -0
  39. package/dist/shared/icons/SortArrowUp.svelte.d.ts +16 -0
  40. package/dist/shared/icons/SortButtonDown.svelte +14 -0
  41. package/dist/shared/icons/SortButtonDown.svelte.d.ts +23 -0
  42. package/dist/shared/icons/SortButtonUp.svelte +15 -0
  43. package/dist/shared/icons/SortButtonUp.svelte.d.ts +23 -0
  44. package/dist/shared/icons/SortIcon.svelte +46 -68
  45. package/dist/shared/icons/SortIcon.svelte.d.ts +3 -2
  46. package/dist/shared/selection_utils.d.ts +2 -1
  47. package/dist/shared/selection_utils.js +39 -10
  48. package/dist/shared/utils/data_processing.d.ts +13 -0
  49. package/dist/shared/utils/data_processing.js +45 -0
  50. package/dist/shared/utils/drag_utils.d.ts +15 -0
  51. package/dist/shared/utils/drag_utils.js +57 -0
  52. package/dist/shared/utils/keyboard_utils.d.ts +2 -0
  53. package/dist/shared/utils/keyboard_utils.js +186 -0
  54. package/dist/shared/utils/sort_utils.d.ts +22 -3
  55. package/dist/shared/utils/sort_utils.js +44 -24
  56. package/dist/shared/utils/table_utils.d.ts +6 -5
  57. package/dist/shared/utils/table_utils.js +13 -56
  58. package/package.json +7 -7
  59. package/shared/CellMenu.svelte +90 -10
  60. package/shared/CellMenuButton.svelte +46 -0
  61. package/shared/CellMenuIcons.svelte +79 -0
  62. package/shared/EditableCell.svelte +97 -18
  63. package/shared/EmptyRowButton.svelte +29 -0
  64. package/shared/RowNumber.svelte +41 -0
  65. package/shared/Table.svelte +604 -1235
  66. package/shared/TableCell.svelte +324 -0
  67. package/shared/TableHeader.svelte +256 -0
  68. package/shared/Toolbar.svelte +19 -8
  69. package/shared/VirtualTable.svelte +72 -19
  70. package/shared/context/keyboard_context.ts +65 -0
  71. package/shared/context/selection_context.ts +168 -0
  72. package/shared/context/table_context.ts +625 -0
  73. package/shared/icons/Padlock.svelte +24 -0
  74. package/shared/icons/SelectionButtons.svelte +93 -0
  75. package/shared/icons/SortArrowDown.svelte +25 -0
  76. package/shared/icons/SortArrowUp.svelte +25 -0
  77. package/shared/icons/SortButtonDown.svelte +14 -0
  78. package/shared/icons/SortButtonUp.svelte +15 -0
  79. package/shared/icons/SortIcon.svelte +47 -70
  80. package/shared/selection_utils.ts +39 -13
  81. package/shared/utils/data_processing.ts +72 -0
  82. package/shared/utils/drag_utils.ts +92 -0
  83. package/shared/utils/keyboard_utils.ts +238 -0
  84. package/shared/utils/sort_utils.test.ts +262 -14
  85. package/shared/utils/sort_utils.ts +67 -31
  86. package/shared/utils/table_utils.test.ts +66 -45
  87. package/shared/utils/table_utils.ts +16 -86
@@ -1,56 +1,39 @@
1
1
  <script>import { createEventDispatcher } from "svelte";
2
+ import SortButtonUp from "./SortButtonUp.svelte";
3
+ import SortButtonDown from "./SortButtonDown.svelte";
4
+ import { IconButton } from "@gradio/atoms";
2
5
  export let direction = null;
6
+ export let priority = null;
3
7
  export let i18n;
4
8
  const dispatch = createEventDispatcher();
5
9
  </script>
6
10
 
7
11
  <div class="sort-icons" role="group" aria-label={i18n("dataframe.sort_column")}>
8
- <button
9
- class="sort-button up"
10
- class:active={direction === "asc"}
11
- on:click={() => dispatch("sort", "asc")}
12
- aria-label={i18n("dataframe.sort_ascending")}
13
- aria-pressed={direction === "asc"}
14
- >
15
- <svg
16
- viewBox="0 0 24 24"
17
- fill="none"
18
- xmlns="http://www.w3.org/2000/svg"
19
- aria-hidden="true"
20
- focusable="false"
12
+ {#if (direction === "asc" || direction === "desc") && priority !== null}
13
+ <span aria-label={`Sort priority: ${priority}`} class="priority"
14
+ >{priority}</span
21
15
  >
22
- <path
23
- d="M7 14l5-5 5 5"
24
- stroke="currentColor"
25
- stroke-width="2"
26
- stroke-linecap="round"
27
- stroke-linejoin="round"
28
- />
29
- </svg>
30
- </button>
31
- <button
32
- class="sort-button down"
33
- class:active={direction === "des"}
34
- on:click={() => dispatch("sort", "des")}
35
- aria-label={i18n("dataframe.sort_descending")}
36
- aria-pressed={direction === "des"}
37
- >
38
- <svg
39
- viewBox="0 0 24 24"
40
- fill="none"
41
- xmlns="http://www.w3.org/2000/svg"
42
- aria-hidden="true"
43
- focusable="false"
44
- >
45
- <path
46
- d="M7 10l5 5 5-5"
47
- stroke="currentColor"
48
- stroke-width="2"
49
- stroke-linecap="round"
50
- stroke-linejoin="round"
51
- />
52
- </svg>
53
- </button>
16
+ {/if}
17
+ <IconButton
18
+ size="x-small"
19
+ label={i18n("dataframe.sort_ascending")}
20
+ Icon={SortButtonUp}
21
+ highlight={direction === "asc"}
22
+ on:click={(event) => {
23
+ event.stopPropagation();
24
+ dispatch("sort", "asc");
25
+ }}
26
+ ></IconButton>
27
+ <IconButton
28
+ size="x-small"
29
+ label={i18n("dataframe.sort_descending")}
30
+ Icon={SortButtonDown}
31
+ highlight={direction === "desc"}
32
+ on:click={(event) => {
33
+ event.stopPropagation();
34
+ dispatch("sort", "desc");
35
+ }}
36
+ ></IconButton>
54
37
  </div>
55
38
 
56
39
  <style>
@@ -58,33 +41,28 @@ const dispatch = createEventDispatcher();
58
41
  display: flex;
59
42
  flex-direction: column;
60
43
  gap: 0;
61
- margin-right: var(--spacing-md);
44
+ margin-right: var(--spacing-sm);
45
+ }
46
+
47
+ .sort-icons :global(button) {
48
+ margin-bottom: var(--spacing-xs);
49
+ border: 1px solid var(--border-color-primary);
50
+ background: unset;
62
51
  }
63
52
 
64
- .sort-button {
53
+ .priority {
65
54
  display: flex;
66
55
  align-items: center;
67
56
  justify-content: center;
68
- padding: 0;
69
- background: none;
70
- border: none;
71
- cursor: pointer;
72
- opacity: 0.5;
73
- transition: opacity 150ms;
74
- }
75
-
76
- .sort-button:hover {
77
- opacity: 0.8;
78
- }
79
-
80
- .sort-button.active {
81
- opacity: 1;
82
- color: var(--color-accent);
83
- }
84
-
85
- svg {
86
- width: var(--size-3);
87
- height: var(--size-3);
88
- display: block;
57
+ position: absolute;
58
+ font-size: var(--size-2);
59
+ left: 19px;
60
+ z-index: var(--layer-3);
61
+ top: var(--spacing-xs);
62
+ background-color: var(--button-secondary-background-fill);
63
+ color: var(--body-text-color);
64
+ border-radius: var(--radius-sm);
65
+ width: var(--size-2-5);
66
+ height: var(--size-2-5);
89
67
  }
90
68
  </style>
@@ -2,11 +2,12 @@ import { SvelteComponent } from "svelte";
2
2
  import type { I18nFormatter } from "@gradio/utils";
3
3
  declare const __propDef: {
4
4
  props: {
5
- direction?: (("asc" | "des") | null) | undefined;
5
+ direction?: (("asc" | "desc") | null) | undefined;
6
+ priority?: (number | null) | undefined;
6
7
  i18n: I18nFormatter;
7
8
  };
8
9
  events: {
9
- sort: CustomEvent<"asc" | "des">;
10
+ sort: CustomEvent<"asc" | "desc">;
10
11
  } & {
11
12
  [evt: string]: CustomEvent<any>;
12
13
  };
@@ -3,6 +3,7 @@ export type CellData = {
3
3
  id: string;
4
4
  value: string | number;
5
5
  };
6
+ export declare function is_cell_in_selection(coords: [number, number], selected_cells: [number, number][]): boolean;
6
7
  export declare function is_cell_selected(cell: CellCoordinate, selected_cells: CellCoordinate[]): string;
7
8
  export declare function get_range_selection(start: CellCoordinate, end: CellCoordinate): CellCoordinate[];
8
9
  export declare function handle_selection(current: CellCoordinate, selected_cells: CellCoordinate[], event: {
@@ -14,7 +15,7 @@ export declare function handle_delete_key(data: CellData[][], selected_cells: Ce
14
15
  export declare function handle_editing_state(current: CellCoordinate, editing: EditingState, selected_cells: CellCoordinate[], editable: boolean): EditingState;
15
16
  export declare function should_show_cell_menu(cell: CellCoordinate, selected_cells: CellCoordinate[], editable: boolean): boolean;
16
17
  export declare function get_next_cell_coordinates(current: CellCoordinate, data: CellData[][], shift_key: boolean): CellCoordinate | false;
17
- export declare function move_cursor(key: "ArrowRight" | "ArrowLeft" | "ArrowDown" | "ArrowUp", current_coords: CellCoordinate, data: CellData[][]): CellCoordinate | false;
18
+ export declare function move_cursor(event: KeyboardEvent, current_coords: CellCoordinate, data: CellData[][]): CellCoordinate | false;
18
19
  export declare function get_current_indices(id: string, data: CellData[][]): [number, number];
19
20
  export declare function handle_click_outside(event: Event, parent: HTMLElement): boolean;
20
21
  export declare function select_column(data: any[][], col: number): CellCoordinate[];
@@ -1,3 +1,7 @@
1
+ export function is_cell_in_selection(coords, selected_cells) {
2
+ const [row, col] = coords;
3
+ return selected_cells.some(([r, c]) => r === row && c === col);
4
+ }
1
5
  export function is_cell_selected(cell, selected_cells) {
2
6
  const [row, col] = cell;
3
7
  if (!selected_cells.some(([r, c]) => r === row && c === col))
@@ -16,8 +20,14 @@ export function get_range_selection(start, end) {
16
20
  const min_col = Math.min(start_col, end_col);
17
21
  const max_col = Math.max(start_col, end_col);
18
22
  const cells = [];
23
+ // Add the start cell as the "anchor" cell so that when
24
+ // we press shift+arrow keys, the selection will always
25
+ // include the anchor cell.
26
+ cells.push(start);
19
27
  for (let i = min_row; i <= max_row; i++) {
20
28
  for (let j = min_col; j <= max_col; j++) {
29
+ if (i === start_row && j === start_col)
30
+ continue;
21
31
  cells.push([i, j]);
22
32
  }
23
33
  }
@@ -81,15 +91,40 @@ export function get_next_cell_coordinates(current, data, shift_key) {
81
91
  }
82
92
  return false;
83
93
  }
84
- export function move_cursor(key, current_coords, data) {
94
+ export function move_cursor(event, current_coords, data) {
95
+ const key = event.key;
85
96
  const dir = {
86
97
  ArrowRight: [0, 1],
87
98
  ArrowLeft: [0, -1],
88
99
  ArrowDown: [1, 0],
89
100
  ArrowUp: [-1, 0]
90
101
  }[key];
91
- const i = current_coords[0] + dir[0];
92
- const j = current_coords[1] + dir[1];
102
+ let i, j;
103
+ if (event.metaKey || event.ctrlKey) {
104
+ if (key === "ArrowRight") {
105
+ i = current_coords[0];
106
+ j = data[0].length - 1;
107
+ }
108
+ else if (key === "ArrowLeft") {
109
+ i = current_coords[0];
110
+ j = 0;
111
+ }
112
+ else if (key === "ArrowDown") {
113
+ i = data.length - 1;
114
+ j = current_coords[1];
115
+ }
116
+ else if (key === "ArrowUp") {
117
+ i = 0;
118
+ j = current_coords[1];
119
+ }
120
+ else {
121
+ return false;
122
+ }
123
+ }
124
+ else {
125
+ i = current_coords[0] + dir[0];
126
+ j = current_coords[1] + dir[1];
127
+ }
93
128
  if (i < 0 && j <= 0) {
94
129
  return false;
95
130
  }
@@ -120,20 +155,14 @@ export function calculate_selection_positions(selected, data, els, parent, table
120
155
  if (!data[row]?.[col]) {
121
156
  return { col_pos: "0px", row_pos: undefined };
122
157
  }
123
- let offset = 0;
124
- for (let i = 0; i < col; i++) {
125
- offset += parseFloat(getComputedStyle(parent).getPropertyValue(`--cell-width-${i}`));
126
- }
127
158
  const cell_id = data[row][col].id;
128
159
  const cell_el = els[cell_id]?.cell;
129
160
  if (!cell_el) {
130
- // if we cant get the row position, just return the column position which is static
131
161
  return { col_pos: "0px", row_pos: undefined };
132
162
  }
133
163
  const cell_rect = cell_el.getBoundingClientRect();
134
164
  const table_rect = table.getBoundingClientRect();
135
165
  const col_pos = `${cell_rect.left - table_rect.left + cell_rect.width / 2}px`;
136
- const relative_top = cell_rect.top - table_rect.top;
137
- const row_pos = `${relative_top + cell_rect.height / 2}px`;
166
+ const row_pos = `${cell_rect.top - table_rect.top + cell_rect.height / 2}px`;
138
167
  return { col_pos, row_pos };
139
168
  }
@@ -0,0 +1,13 @@
1
+ import type { Headers, HeadersWithIDs } from "../utils";
2
+ export declare function make_headers(_head: Headers, col_count: [number, "fixed" | "dynamic"], els: Record<string, {
3
+ cell: null | HTMLTableCellElement;
4
+ input: null | HTMLInputElement;
5
+ }>, make_id: () => string): HeadersWithIDs;
6
+ export declare function process_data(values: (string | number)[][], els: Record<string, {
7
+ cell: null | HTMLTableCellElement;
8
+ input: null | HTMLInputElement;
9
+ }>, data_binding: Record<string, any>, make_id: () => string, display_value?: string[][] | null): {
10
+ id: string;
11
+ value: string | number;
12
+ display_value?: string;
13
+ }[][];
@@ -0,0 +1,45 @@
1
+ export function make_headers(_head, col_count, els, make_id) {
2
+ let _h = _head || [];
3
+ if (col_count[1] === "fixed" && _h.length < col_count[0]) {
4
+ const fill = Array(col_count[0] - _h.length)
5
+ .fill("")
6
+ .map((_, i) => `${i + _h.length}`);
7
+ _h = _h.concat(fill);
8
+ }
9
+ if (!_h || _h.length === 0) {
10
+ return Array(col_count[0])
11
+ .fill(0)
12
+ .map((_, i) => {
13
+ const _id = make_id();
14
+ els[_id] = { cell: null, input: null };
15
+ return { id: _id, value: JSON.stringify(i + 1) };
16
+ });
17
+ }
18
+ return _h.map((h, i) => {
19
+ const _id = make_id();
20
+ els[_id] = { cell: null, input: null };
21
+ return { id: _id, value: h ?? "" };
22
+ });
23
+ }
24
+ export function process_data(values, els, data_binding, make_id, display_value = null) {
25
+ if (!values || values.length === 0) {
26
+ return [];
27
+ }
28
+ const result = values.map((row, i) => {
29
+ return row.map((value, j) => {
30
+ const _id = make_id();
31
+ els[_id] = { cell: null, input: null };
32
+ data_binding[_id] = value;
33
+ let display = display_value?.[i]?.[j];
34
+ if (display === undefined) {
35
+ display = String(value);
36
+ }
37
+ return {
38
+ id: _id,
39
+ value,
40
+ display_value: display
41
+ };
42
+ });
43
+ });
44
+ return result;
45
+ }
@@ -0,0 +1,15 @@
1
+ import type { CellCoordinate } from "../types";
2
+ export type DragState = {
3
+ is_dragging: boolean;
4
+ drag_start: CellCoordinate | null;
5
+ mouse_down_pos: {
6
+ x: number;
7
+ y: number;
8
+ } | null;
9
+ };
10
+ export type DragHandlers = {
11
+ handle_mouse_down: (event: MouseEvent, row: number, col: number) => void;
12
+ handle_mouse_move: (event: MouseEvent) => void;
13
+ handle_mouse_up: (event: MouseEvent) => void;
14
+ };
15
+ export declare function create_drag_handlers(state: DragState, set_is_dragging: (value: boolean) => void, set_selected_cells: (cells: CellCoordinate[]) => void, set_selected: (cell: CellCoordinate | false) => void, handle_cell_click: (event: MouseEvent, row: number, col: number) => void, show_row_numbers: boolean, parent_element?: HTMLElement): DragHandlers;
@@ -0,0 +1,57 @@
1
+ import { get_range_selection } from "../selection_utils";
2
+ export function create_drag_handlers(state, set_is_dragging, set_selected_cells, set_selected, handle_cell_click, show_row_numbers, parent_element) {
3
+ const start_drag = (event, row, col) => {
4
+ if (event.target instanceof HTMLAnchorElement ||
5
+ (show_row_numbers && col === -1))
6
+ return;
7
+ event.preventDefault();
8
+ event.stopPropagation();
9
+ state.mouse_down_pos = { x: event.clientX, y: event.clientY };
10
+ state.drag_start = [row, col];
11
+ if (!event.shiftKey && !event.metaKey && !event.ctrlKey) {
12
+ set_selected_cells([[row, col]]);
13
+ set_selected([row, col]);
14
+ }
15
+ };
16
+ const update_selection = (event) => {
17
+ const cell = event.target.closest("td");
18
+ if (!cell)
19
+ return;
20
+ const row = parseInt(cell.getAttribute("data-row") || "0");
21
+ const col = parseInt(cell.getAttribute("data-col") || "0");
22
+ if (isNaN(row) || isNaN(col))
23
+ return;
24
+ const selection_range = get_range_selection(state.drag_start, [row, col]);
25
+ set_selected_cells(selection_range);
26
+ set_selected([row, col]);
27
+ };
28
+ const end_drag = (event) => {
29
+ if (!state.is_dragging && state.drag_start) {
30
+ handle_cell_click(event, state.drag_start[0], state.drag_start[1]);
31
+ }
32
+ else if (state.is_dragging && parent_element) {
33
+ parent_element.focus();
34
+ }
35
+ state.is_dragging = false;
36
+ set_is_dragging(false);
37
+ state.drag_start = null;
38
+ state.mouse_down_pos = null;
39
+ };
40
+ return {
41
+ handle_mouse_down: start_drag,
42
+ handle_mouse_move(event) {
43
+ if (!state.drag_start || !state.mouse_down_pos)
44
+ return;
45
+ const dx = Math.abs(event.clientX - state.mouse_down_pos.x);
46
+ const dy = Math.abs(event.clientY - state.mouse_down_pos.y);
47
+ if (!state.is_dragging && (dx > 3 || dy > 3)) {
48
+ state.is_dragging = true;
49
+ set_is_dragging(true);
50
+ }
51
+ if (state.is_dragging) {
52
+ update_selection(event);
53
+ }
54
+ },
55
+ handle_mouse_up: end_drag
56
+ };
57
+ }
@@ -0,0 +1,2 @@
1
+ import type { KeyboardContext } from "../context/keyboard_context";
2
+ export declare function handle_keydown(event: KeyboardEvent, context: KeyboardContext): Promise<void>;
@@ -0,0 +1,186 @@
1
+ import { dequal } from "dequal/lite";
2
+ import { handle_delete_key } from "../selection_utils";
3
+ import { tick } from "svelte";
4
+ import { copy_table_data } from "./table_utils";
5
+ function handle_header_navigation(event, ctx) {
6
+ if (ctx.selected_header === false || ctx.header_edit !== false)
7
+ return false;
8
+ switch (event.key) {
9
+ case "ArrowDown":
10
+ ctx.df_actions.set_selected_header(false);
11
+ ctx.df_actions.set_selected([0, ctx.selected_header]);
12
+ ctx.df_actions.set_selected_cells([[0, ctx.selected_header]]);
13
+ return true;
14
+ case "ArrowLeft":
15
+ ctx.df_actions.set_selected_header(ctx.selected_header > 0 ? ctx.selected_header - 1 : ctx.selected_header);
16
+ return true;
17
+ case "ArrowRight":
18
+ ctx.df_actions.set_selected_header(ctx.selected_header < ctx.headers.length - 1
19
+ ? ctx.selected_header + 1
20
+ : ctx.selected_header);
21
+ return true;
22
+ case "Escape":
23
+ event.preventDefault();
24
+ ctx.df_actions.set_selected_header(false);
25
+ return true;
26
+ case "Enter":
27
+ event.preventDefault();
28
+ if (ctx.editable) {
29
+ ctx.df_actions.set_header_edit(ctx.selected_header);
30
+ }
31
+ return true;
32
+ }
33
+ return false;
34
+ }
35
+ function handle_delete_operation(event, ctx) {
36
+ if (!ctx.editable)
37
+ return false;
38
+ if (event.key !== "Delete" && event.key !== "Backspace")
39
+ return false;
40
+ if (ctx.editing) {
41
+ const [row, col] = ctx.editing;
42
+ const input_el = ctx.els[ctx.data[row][col].id].input;
43
+ if (input_el && input_el.selectionStart !== input_el.selectionEnd) {
44
+ return false;
45
+ }
46
+ if (event.key === "Delete" &&
47
+ input_el?.selectionStart !== input_el?.value.length) {
48
+ return false;
49
+ }
50
+ if (event.key === "Backspace" && input_el?.selectionStart !== 0) {
51
+ return false;
52
+ }
53
+ }
54
+ event.preventDefault();
55
+ if (ctx.selected_cells.length > 0) {
56
+ const new_data = handle_delete_key(ctx.data, ctx.selected_cells);
57
+ ctx.dispatch("change", {
58
+ data: new_data.map((row) => row.map((cell) => cell.value)),
59
+ headers: ctx.headers.map((h) => h.value),
60
+ metadata: null
61
+ });
62
+ ctx.dispatch("input");
63
+ }
64
+ return true;
65
+ }
66
+ function handle_arrow_keys(event, ctx, i, j) {
67
+ if (ctx.editing)
68
+ return false;
69
+ event.preventDefault();
70
+ const next_coords = ctx.move_cursor(event, [i, j], ctx.data);
71
+ if (next_coords) {
72
+ if (event.shiftKey) {
73
+ ctx.df_actions.set_selected_cells(ctx.get_range_selection(ctx.selected_cells.length > 0 ? ctx.selected_cells[0] : [i, j], next_coords));
74
+ ctx.df_actions.set_editing(false);
75
+ }
76
+ else {
77
+ ctx.df_actions.set_selected_cells([next_coords]);
78
+ ctx.df_actions.set_editing(false);
79
+ }
80
+ ctx.df_actions.set_selected(next_coords);
81
+ }
82
+ else if (next_coords === false && event.key === "ArrowUp" && i === 0) {
83
+ ctx.df_actions.set_selected_header(j);
84
+ ctx.df_actions.set_selected(false);
85
+ ctx.df_actions.set_selected_cells([]);
86
+ ctx.df_actions.set_editing(false);
87
+ }
88
+ return true;
89
+ }
90
+ async function handle_enter_key(event, ctx, i, j) {
91
+ event.preventDefault();
92
+ if (!ctx.editable)
93
+ return false;
94
+ if (event.shiftKey) {
95
+ await ctx.add_row(i);
96
+ await tick();
97
+ ctx.df_actions.set_selected([i + 1, j]);
98
+ }
99
+ else {
100
+ if (dequal(ctx.editing, [i, j])) {
101
+ const cell_id = ctx.data[i][j].id;
102
+ const input_el = ctx.els[cell_id].input;
103
+ if (input_el) {
104
+ const old_value = ctx.data[i][j].value;
105
+ ctx.data[i][j].value = input_el.value;
106
+ if (old_value !== input_el.value) {
107
+ ctx.dispatch("input");
108
+ }
109
+ }
110
+ ctx.df_actions.set_editing(false);
111
+ await tick();
112
+ ctx.df_actions.set_selected([i, j]);
113
+ }
114
+ else {
115
+ ctx.df_actions.set_editing([i, j]);
116
+ }
117
+ }
118
+ return true;
119
+ }
120
+ function handle_tab_key(event, ctx, i, j) {
121
+ event.preventDefault();
122
+ ctx.df_actions.set_editing(false);
123
+ const next_cell = ctx.get_next_cell_coordinates([i, j], ctx.data, event.shiftKey);
124
+ if (next_cell) {
125
+ ctx.df_actions.set_selected_cells([next_cell]);
126
+ ctx.df_actions.set_selected(next_cell);
127
+ if (ctx.editable) {
128
+ ctx.df_actions.set_editing(next_cell);
129
+ }
130
+ }
131
+ return true;
132
+ }
133
+ function handle_default_key(event, ctx, i, j) {
134
+ if (!ctx.editable)
135
+ return false;
136
+ if ((!ctx.editing || (ctx.editing && dequal(ctx.editing, [i, j]))) &&
137
+ event.key.length === 1) {
138
+ ctx.df_actions.set_editing([i, j]);
139
+ return true;
140
+ }
141
+ return false;
142
+ }
143
+ async function handle_cell_navigation(event, ctx) {
144
+ if (!ctx.selected)
145
+ return false;
146
+ if (event.key === "c" && (event.metaKey || event.ctrlKey)) {
147
+ event.preventDefault();
148
+ if (ctx.selected_cells.length > 0) {
149
+ await copy_table_data(ctx.data, ctx.selected_cells);
150
+ }
151
+ ctx.set_copy_flash(true);
152
+ return true;
153
+ }
154
+ const [i, j] = ctx.selected;
155
+ switch (event.key) {
156
+ case "ArrowRight":
157
+ case "ArrowLeft":
158
+ case "ArrowDown":
159
+ case "ArrowUp":
160
+ return handle_arrow_keys(event, ctx, i, j);
161
+ case "Escape":
162
+ if (!ctx.editable)
163
+ return false;
164
+ event.preventDefault();
165
+ ctx.df_actions.set_editing(false);
166
+ tick().then(() => {
167
+ if (ctx.parent_element) {
168
+ ctx.parent_element.focus();
169
+ }
170
+ });
171
+ return true;
172
+ case "Enter":
173
+ return await handle_enter_key(event, ctx, i, j);
174
+ case "Tab":
175
+ return handle_tab_key(event, ctx, i, j);
176
+ default:
177
+ return handle_default_key(event, ctx, i, j);
178
+ }
179
+ }
180
+ export async function handle_keydown(event, context) {
181
+ if (handle_header_navigation(event, context))
182
+ return;
183
+ if (handle_delete_operation(event, context))
184
+ return;
185
+ await handle_cell_navigation(event, context);
186
+ }
@@ -1,7 +1,26 @@
1
1
  import type { Headers } from "../types";
2
- export type SortDirection = "asc" | "des";
3
- export declare function get_sort_status(name: string, sort_by: number | undefined, direction: SortDirection | undefined, headers: Headers): "none" | "ascending" | "descending";
2
+ export type SortDirection = "asc" | "desc";
3
+ export declare function get_sort_status(name: string, sort_columns: {
4
+ col: number;
5
+ direction: SortDirection;
6
+ }[], headers: Headers): "none" | "asc" | "desc";
4
7
  export declare function sort_data(data: {
5
8
  id: string;
6
9
  value: string | number;
7
- }[][], sort_by: number | undefined, sort_direction: SortDirection | undefined): number[];
10
+ }[][], sort_columns: {
11
+ col: number;
12
+ direction: SortDirection;
13
+ }[]): number[];
14
+ export declare function sort_data_and_preserve_selection(data: {
15
+ id: string;
16
+ value: string | number;
17
+ }[][], display_value: string[][] | null, styling: string[][] | null, sort_columns: {
18
+ col: number;
19
+ direction: SortDirection;
20
+ }[], selected: [number, number] | false, get_current_indices: (id: string, data: {
21
+ id: string;
22
+ value: string | number;
23
+ }[][]) => [number, number]): {
24
+ data: typeof data;
25
+ selected: [number, number] | false;
26
+ };