@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.
- package/CHANGELOG.md +34 -0
- package/Dataframe.stories.svelte +202 -9
- package/Index.svelte +7 -13
- package/dist/Index.svelte +5 -9
- package/dist/Index.svelte.d.ts +9 -2
- package/dist/shared/CellMenu.svelte +91 -10
- package/dist/shared/CellMenu.svelte.d.ts +6 -0
- package/dist/shared/CellMenuButton.svelte +45 -0
- package/dist/shared/CellMenuButton.svelte.d.ts +16 -0
- package/dist/shared/CellMenuIcons.svelte +79 -0
- package/dist/shared/EditableCell.svelte +83 -14
- package/dist/shared/EditableCell.svelte.d.ts +12 -3
- package/dist/shared/EmptyRowButton.svelte +28 -0
- package/dist/shared/EmptyRowButton.svelte.d.ts +16 -0
- package/dist/shared/RowNumber.svelte +40 -0
- package/dist/shared/RowNumber.svelte.d.ts +17 -0
- package/dist/shared/Table.svelte +564 -1121
- package/dist/shared/Table.svelte.d.ts +4 -0
- package/dist/shared/TableCell.svelte +291 -0
- package/dist/shared/TableCell.svelte.d.ts +57 -0
- package/dist/shared/TableHeader.svelte +239 -0
- package/dist/shared/TableHeader.svelte.d.ts +45 -0
- package/dist/shared/Toolbar.svelte +18 -8
- package/dist/shared/VirtualTable.svelte +66 -19
- package/dist/shared/VirtualTable.svelte.d.ts +4 -0
- package/dist/shared/context/keyboard_context.d.ts +37 -0
- package/dist/shared/context/keyboard_context.js +12 -0
- package/dist/shared/context/selection_context.d.ts +32 -0
- package/dist/shared/context/selection_context.js +107 -0
- package/dist/shared/context/table_context.d.ts +141 -0
- package/dist/shared/context/table_context.js +375 -0
- package/dist/shared/icons/Padlock.svelte +24 -0
- package/dist/shared/icons/Padlock.svelte.d.ts +23 -0
- package/dist/shared/icons/SelectionButtons.svelte +85 -0
- package/dist/shared/icons/SelectionButtons.svelte.d.ts +18 -0
- package/dist/shared/icons/SortArrowDown.svelte +24 -0
- package/dist/shared/icons/SortArrowDown.svelte.d.ts +16 -0
- package/dist/shared/icons/SortArrowUp.svelte +24 -0
- package/dist/shared/icons/SortArrowUp.svelte.d.ts +16 -0
- package/dist/shared/icons/SortButtonDown.svelte +14 -0
- package/dist/shared/icons/SortButtonDown.svelte.d.ts +23 -0
- package/dist/shared/icons/SortButtonUp.svelte +15 -0
- package/dist/shared/icons/SortButtonUp.svelte.d.ts +23 -0
- package/dist/shared/icons/SortIcon.svelte +46 -68
- package/dist/shared/icons/SortIcon.svelte.d.ts +3 -2
- package/dist/shared/selection_utils.d.ts +2 -1
- package/dist/shared/selection_utils.js +39 -10
- package/dist/shared/utils/data_processing.d.ts +13 -0
- package/dist/shared/utils/data_processing.js +45 -0
- package/dist/shared/utils/drag_utils.d.ts +15 -0
- package/dist/shared/utils/drag_utils.js +57 -0
- package/dist/shared/utils/keyboard_utils.d.ts +2 -0
- package/dist/shared/utils/keyboard_utils.js +186 -0
- package/dist/shared/utils/sort_utils.d.ts +22 -3
- package/dist/shared/utils/sort_utils.js +44 -24
- package/dist/shared/utils/table_utils.d.ts +6 -5
- package/dist/shared/utils/table_utils.js +13 -56
- package/package.json +7 -7
- package/shared/CellMenu.svelte +90 -10
- package/shared/CellMenuButton.svelte +46 -0
- package/shared/CellMenuIcons.svelte +79 -0
- package/shared/EditableCell.svelte +97 -18
- package/shared/EmptyRowButton.svelte +29 -0
- package/shared/RowNumber.svelte +41 -0
- package/shared/Table.svelte +604 -1235
- package/shared/TableCell.svelte +324 -0
- package/shared/TableHeader.svelte +256 -0
- package/shared/Toolbar.svelte +19 -8
- package/shared/VirtualTable.svelte +72 -19
- package/shared/context/keyboard_context.ts +65 -0
- package/shared/context/selection_context.ts +168 -0
- package/shared/context/table_context.ts +625 -0
- package/shared/icons/Padlock.svelte +24 -0
- package/shared/icons/SelectionButtons.svelte +93 -0
- package/shared/icons/SortArrowDown.svelte +25 -0
- package/shared/icons/SortArrowUp.svelte +25 -0
- package/shared/icons/SortButtonDown.svelte +14 -0
- package/shared/icons/SortButtonUp.svelte +15 -0
- package/shared/icons/SortIcon.svelte +47 -70
- package/shared/selection_utils.ts +39 -13
- package/shared/utils/data_processing.ts +72 -0
- package/shared/utils/drag_utils.ts +92 -0
- package/shared/utils/keyboard_utils.ts +238 -0
- package/shared/utils/sort_utils.test.ts +262 -14
- package/shared/utils/sort_utils.ts +67 -31
- package/shared/utils/table_utils.test.ts +66 -45
- 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
|
-
|
|
9
|
-
class="
|
|
10
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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-
|
|
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
|
-
.
|
|
53
|
+
.priority {
|
|
65
54
|
display: flex;
|
|
66
55
|
align-items: center;
|
|
67
56
|
justify-content: center;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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" | "
|
|
5
|
+
direction?: (("asc" | "desc") | null) | undefined;
|
|
6
|
+
priority?: (number | null) | undefined;
|
|
6
7
|
i18n: I18nFormatter;
|
|
7
8
|
};
|
|
8
9
|
events: {
|
|
9
|
-
sort: CustomEvent<"asc" | "
|
|
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(
|
|
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(
|
|
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
|
-
|
|
92
|
-
|
|
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
|
|
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,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" | "
|
|
3
|
-
export declare function get_sort_status(name: string,
|
|
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
|
-
}[][],
|
|
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
|
+
};
|