@gradio/dataframe 0.22.0 → 0.23.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 (128) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/Dataframe.test.ts +777 -31
  3. package/Index.svelte +107 -106
  4. package/dist/Index.svelte +107 -106
  5. package/dist/Index.svelte.d.ts +0 -1
  6. package/dist/shared/BooleanCell.svelte +12 -6
  7. package/dist/shared/BooleanCell.svelte.d.ts +4 -18
  8. package/dist/shared/CellMenu.svelte +74 -49
  9. package/dist/shared/CellMenu.svelte.d.ts +5 -19
  10. package/dist/shared/CellMenuButton.svelte +3 -3
  11. package/dist/shared/CellMenuButton.svelte.d.ts +4 -18
  12. package/dist/shared/CellMenuIcons.svelte +1 -1
  13. package/dist/shared/CellMenuIcons.svelte.d.ts +4 -18
  14. package/dist/shared/DataCell.svelte +189 -0
  15. package/dist/shared/DataCell.svelte.d.ts +44 -0
  16. package/dist/shared/EditableCell.svelte +88 -64
  17. package/dist/shared/EditableCell.svelte.d.ts +8 -26
  18. package/dist/shared/EmptyRowButton.svelte +2 -2
  19. package/dist/shared/EmptyRowButton.svelte.d.ts +4 -18
  20. package/dist/shared/FilterMenu.svelte +26 -18
  21. package/dist/shared/FilterMenu.svelte.d.ts +5 -21
  22. package/dist/shared/HeaderCell.svelte +218 -0
  23. package/dist/shared/HeaderCell.svelte.d.ts +30 -0
  24. package/dist/shared/RowNumber.svelte +4 -2
  25. package/dist/shared/RowNumber.svelte.d.ts +4 -18
  26. package/dist/shared/Table.svelte +1037 -949
  27. package/dist/shared/Table.svelte.d.ts +12 -30
  28. package/dist/shared/Toolbar.svelte +32 -22
  29. package/dist/shared/Toolbar.svelte.d.ts +6 -21
  30. package/dist/shared/column_measurement.svelte.d.ts +20 -0
  31. package/dist/shared/column_measurement.svelte.js +79 -0
  32. package/dist/shared/icons/FilterIcon.svelte +14 -6
  33. package/dist/shared/icons/FilterIcon.svelte.d.ts +14 -6
  34. package/dist/shared/icons/Padlock.svelte +6 -2
  35. package/dist/shared/icons/Padlock.svelte.d.ts +4 -25
  36. package/dist/shared/icons/SelectionButtons.svelte +23 -8
  37. package/dist/shared/icons/SelectionButtons.svelte.d.ts +4 -18
  38. package/dist/shared/icons/SortButtonDown.svelte +15 -4
  39. package/dist/shared/icons/SortButtonDown.svelte.d.ts +4 -25
  40. package/dist/shared/icons/SortButtonUp.svelte +15 -5
  41. package/dist/shared/icons/SortButtonUp.svelte.d.ts +4 -25
  42. package/dist/shared/icons/SortIcon.svelte +13 -7
  43. package/dist/shared/icons/SortIcon.svelte.d.ts +7 -21
  44. package/dist/shared/tanstack/index.d.ts +3 -0
  45. package/dist/shared/tanstack/index.js +3 -0
  46. package/dist/shared/tanstack/table.svelte.d.ts +26 -0
  47. package/dist/shared/tanstack/table.svelte.js +102 -0
  48. package/dist/shared/tanstack/virtual.svelte.d.ts +14 -0
  49. package/dist/shared/tanstack/virtual.svelte.js +76 -0
  50. package/dist/shared/types.d.ts +2 -0
  51. package/dist/shared/utils/filter.d.ts +5 -0
  52. package/dist/shared/utils/filter.js +62 -0
  53. package/dist/shared/utils/selection_utils.js +1 -1
  54. package/dist/shared/utils/table_utils.d.ts +1 -14
  55. package/dist/shared/utils/table_utils.js +0 -61
  56. package/dist/standalone/Index.svelte +71 -46
  57. package/dist/standalone/Index.svelte.d.ts +4 -23
  58. package/dist/types.d.ts +32 -0
  59. package/dist/types.js +1 -0
  60. package/package.json +12 -11
  61. package/shared/BooleanCell.svelte +12 -6
  62. package/shared/CellMenu.svelte +74 -49
  63. package/shared/CellMenuButton.svelte +3 -3
  64. package/shared/CellMenuIcons.svelte +1 -1
  65. package/shared/DataCell.svelte +189 -0
  66. package/shared/EditableCell.svelte +88 -64
  67. package/shared/EmptyRowButton.svelte +2 -2
  68. package/shared/FilterMenu.svelte +26 -18
  69. package/shared/HeaderCell.svelte +218 -0
  70. package/shared/RowNumber.svelte +4 -2
  71. package/shared/Table.svelte +1037 -949
  72. package/shared/Toolbar.svelte +32 -22
  73. package/shared/column_measurement.svelte.ts +103 -0
  74. package/shared/icons/FilterIcon.svelte +14 -6
  75. package/shared/icons/Padlock.svelte +6 -2
  76. package/shared/icons/SelectionButtons.svelte +23 -8
  77. package/shared/icons/SortButtonDown.svelte +15 -4
  78. package/shared/icons/SortButtonUp.svelte +15 -5
  79. package/shared/icons/SortIcon.svelte +13 -7
  80. package/shared/tanstack/index.ts +23 -0
  81. package/shared/tanstack/table.svelte.ts +117 -0
  82. package/shared/tanstack/virtual.svelte.ts +105 -0
  83. package/shared/types.ts +3 -0
  84. package/shared/utils/filter.ts +66 -0
  85. package/shared/utils/selection_utils.ts +1 -1
  86. package/shared/utils/table_utils.ts +1 -100
  87. package/standalone/Index.svelte +71 -46
  88. package/test/filter.test.ts +350 -0
  89. package/test/selection_utils.test.ts +416 -0
  90. package/test/table_utils.test.ts +45 -112
  91. package/types.ts +34 -0
  92. package/dist/shared/TableCell.svelte +0 -330
  93. package/dist/shared/TableCell.svelte.d.ts +0 -66
  94. package/dist/shared/TableHeader.svelte +0 -344
  95. package/dist/shared/TableHeader.svelte.d.ts +0 -61
  96. package/dist/shared/VirtualTable.svelte +0 -379
  97. package/dist/shared/VirtualTable.svelte.d.ts +0 -42
  98. package/dist/shared/context/dataframe_context.d.ts +0 -180
  99. package/dist/shared/context/dataframe_context.js +0 -412
  100. package/dist/shared/icons/SortArrowDown.svelte +0 -25
  101. package/dist/shared/icons/SortArrowDown.svelte.d.ts +0 -20
  102. package/dist/shared/icons/SortArrowUp.svelte +0 -25
  103. package/dist/shared/icons/SortArrowUp.svelte.d.ts +0 -20
  104. package/dist/shared/utils/data_processing.d.ts +0 -14
  105. package/dist/shared/utils/data_processing.js +0 -47
  106. package/dist/shared/utils/drag_utils.d.ts +0 -15
  107. package/dist/shared/utils/drag_utils.js +0 -67
  108. package/dist/shared/utils/filter_utils.d.ts +0 -29
  109. package/dist/shared/utils/filter_utils.js +0 -123
  110. package/dist/shared/utils/keyboard_utils.d.ts +0 -3
  111. package/dist/shared/utils/keyboard_utils.js +0 -236
  112. package/dist/shared/utils/menu_utils.d.ts +0 -42
  113. package/dist/shared/utils/menu_utils.js +0 -58
  114. package/dist/shared/utils/sort_utils.d.ts +0 -26
  115. package/dist/shared/utils/sort_utils.js +0 -59
  116. package/shared/TableCell.svelte +0 -330
  117. package/shared/TableHeader.svelte +0 -344
  118. package/shared/VirtualTable.svelte +0 -379
  119. package/shared/context/dataframe_context.ts +0 -701
  120. package/shared/icons/SortArrowDown.svelte +0 -25
  121. package/shared/icons/SortArrowUp.svelte +0 -25
  122. package/shared/utils/data_processing.ts +0 -77
  123. package/shared/utils/drag_utils.ts +0 -105
  124. package/shared/utils/filter_utils.ts +0 -208
  125. package/shared/utils/keyboard_utils.ts +0 -309
  126. package/shared/utils/menu_utils.ts +0 -115
  127. package/shared/utils/sort_utils.ts +0 -91
  128. package/test/sort_utils.test.ts +0 -323
@@ -1,701 +0,0 @@
1
- import { getContext, setContext } from "svelte";
2
- import { dequal } from "dequal";
3
- import { writable, get } from "svelte/store";
4
- import { sort_table_data } from "../utils/table_utils";
5
- import type { CellValue } from "../types";
6
- import { tick } from "svelte";
7
- import {
8
- handle_selection,
9
- get_next_cell_coordinates,
10
- get_range_selection,
11
- move_cursor
12
- } from "../utils/selection_utils";
13
-
14
- export const DATAFRAME_KEY = Symbol("dataframe");
15
-
16
- export type SortDirection = "asc" | "desc";
17
- export type FilterDatatype = "string" | "number";
18
- export type CellCoordinate = [number, number];
19
-
20
- interface DataFrameState {
21
- config: {
22
- show_fullscreen_button: boolean;
23
- show_copy_button: boolean;
24
- show_search: "none" | "search" | "filter";
25
- show_row_numbers: boolean;
26
- editable: boolean;
27
- pinned_columns: number;
28
- show_label: boolean;
29
- line_breaks: boolean;
30
- wrap: boolean;
31
- max_height: number;
32
- column_widths: string[];
33
- max_chars?: number;
34
- static_columns?: CellValue[];
35
- };
36
- current_search_query: string | null;
37
- sort_state: {
38
- sort_columns: { col: number; direction: SortDirection }[];
39
- row_order: number[];
40
- initial_data: {
41
- data: { id: string; value: CellValue }[][];
42
- display_value: string[][] | null;
43
- styling: string[][] | null;
44
- } | null;
45
- };
46
- filter_state: {
47
- filter_columns: {
48
- col: number;
49
- datatype: FilterDatatype;
50
- filter: string;
51
- value: string;
52
- }[];
53
- initial_data: {
54
- data: { id: string; value: CellValue }[][];
55
- display_value: string[][] | null;
56
- styling: string[][] | null;
57
- } | null;
58
- };
59
- ui_state: {
60
- active_cell_menu: { row: number; col: number; x: number; y: number } | null;
61
- active_header_menu: { col: number; x: number; y: number } | null;
62
- selected_cells: CellCoordinate[];
63
- selected: CellCoordinate | false;
64
- editing: CellCoordinate | false;
65
- header_edit: number | false;
66
- selected_header: number | false;
67
- active_button: {
68
- type: "header" | "cell";
69
- row?: number;
70
- col: number;
71
- } | null;
72
- copy_flash: boolean;
73
- };
74
- }
75
-
76
- interface DataFrameActions {
77
- handle_search: (query: string | null) => void;
78
- handle_sort: (col: number, direction: SortDirection) => void;
79
- handle_filter: (
80
- col: number,
81
- datatype: FilterDatatype,
82
- filter: string,
83
- value: string
84
- ) => void;
85
- get_sort_status: (name: string, headers: string[]) => "none" | "asc" | "desc";
86
- sort_data: (
87
- data: any[][],
88
- display_value: string[][] | null,
89
- styling: string[][] | null
90
- ) => void;
91
- update_row_order: (data: any[][]) => void;
92
- filter_data: (data: any[][]) => any[][];
93
- add_row: (data: any[][], make_id: () => string, index?: number) => any[][];
94
- add_col: (
95
- data: any[][],
96
- headers: string[],
97
- make_id: () => string,
98
- index?: number
99
- ) => { data: any[][]; headers: string[] };
100
- add_row_at: (
101
- data: any[][],
102
- index: number,
103
- position: "above" | "below",
104
- make_id: () => string
105
- ) => any[][];
106
- add_col_at: (
107
- data: any[][],
108
- headers: string[],
109
- index: number,
110
- position: "left" | "right",
111
- make_id: () => string
112
- ) => { data: any[][]; headers: string[] };
113
- delete_row: (data: any[][], index: number) => any[][];
114
- delete_col: (
115
- data: any[][],
116
- headers: string[],
117
- index: number
118
- ) => { data: any[][]; headers: string[] };
119
- delete_row_at: (data: any[][], index: number) => any[][];
120
- delete_col_at: (
121
- data: any[][],
122
- headers: string[],
123
- index: number
124
- ) => { data: any[][]; headers: string[] };
125
- trigger_change: (
126
- data: any[][],
127
- headers: any[],
128
- previous_data: any[][],
129
- previous_headers: string[],
130
- value_is_output: boolean,
131
- dispatch: (e: "change" | "input" | "edit", detail?: any) => void
132
- ) => Promise<void>;
133
- reset_sort_state: () => void;
134
- reset_filter_state: () => void;
135
- set_active_cell_menu: (
136
- menu: { row: number; col: number; x: number; y: number } | null
137
- ) => void;
138
- set_active_header_menu: (
139
- menu: { col: number; x: number; y: number } | null
140
- ) => void;
141
- set_selected_cells: (cells: CellCoordinate[]) => void;
142
- set_selected: (selected: CellCoordinate | false) => void;
143
- set_editing: (editing: CellCoordinate | false) => void;
144
- clear_ui_state: () => void;
145
- set_header_edit: (header_index: number | false) => void;
146
- set_selected_header: (header_index: number | false) => void;
147
- handle_header_click: (col: number, editable: boolean) => void;
148
- end_header_edit: (key: string) => void;
149
- get_selected_cells: () => CellCoordinate[];
150
- get_active_cell_menu: () => {
151
- row: number;
152
- col: number;
153
- x: number;
154
- y: number;
155
- } | null;
156
- get_active_button: () => {
157
- type: "header" | "cell";
158
- row?: number;
159
- col: number;
160
- } | null;
161
- set_active_button: (
162
- button: { type: "header" | "cell"; row?: number; col: number } | null
163
- ) => void;
164
- set_copy_flash: (value: boolean) => void;
165
- handle_cell_click: (event: MouseEvent, row: number, col: number) => void;
166
- toggle_cell_menu: (event: MouseEvent, row: number, col: number) => void;
167
- toggle_cell_button: (row: number, col: number) => void;
168
- handle_select_column: (col: number) => void;
169
- handle_select_row: (row: number) => void;
170
- get_next_cell_coordinates: typeof get_next_cell_coordinates;
171
- get_range_selection: typeof get_range_selection;
172
- move_cursor: typeof move_cursor;
173
- }
174
-
175
- export interface DataFrameContext {
176
- state: ReturnType<typeof writable<DataFrameState>>;
177
- actions: DataFrameActions;
178
- data?: any[][];
179
- headers?: { id: string; value: string }[];
180
- display_value?: string[][] | null;
181
- styling?: string[][] | null;
182
- els?: Record<
183
- string,
184
- { cell: HTMLTableCellElement | null; input: HTMLTextAreaElement | null }
185
- >;
186
- parent_element?: HTMLElement;
187
- get_data_at?: (row: number, col: number) => CellValue;
188
- get_column?: (col: number) => CellValue[];
189
- get_row?: (row: number) => CellValue[];
190
- dispatch?: (e: "change" | "select" | "search" | "edit", detail?: any) => void;
191
- }
192
-
193
- function create_actions(
194
- state: ReturnType<typeof writable<DataFrameState>>,
195
- context: DataFrameContext
196
- ): DataFrameActions {
197
- const update_state = (
198
- updater: (s: DataFrameState) => Partial<DataFrameState>
199
- ): void => state.update((s) => ({ ...s, ...updater(s) }));
200
-
201
- const add_row = (
202
- data: any[][],
203
- make_id: () => string,
204
- index?: number
205
- ): any[][] => {
206
- const new_row = data[0]?.length
207
- ? Array(data[0].length)
208
- .fill(null)
209
- .map(() => ({ value: "", id: make_id() }))
210
- : [{ value: "", id: make_id() }];
211
- const new_data = [...data];
212
- index !== undefined
213
- ? new_data.splice(index, 0, new_row)
214
- : new_data.push(new_row);
215
- return new_data;
216
- };
217
-
218
- const add_col = (
219
- data: any[][],
220
- headers: string[],
221
- make_id: () => string,
222
- index?: number
223
- ): { data: any[][]; headers: string[] } => {
224
- const new_headers = [...headers, `Header ${headers.length + 1}`];
225
- const new_data = data.map((row) => [...row, { value: "", id: make_id() }]);
226
- if (index !== undefined) {
227
- new_headers.splice(index, 0, new_headers.pop()!);
228
- new_data.forEach((row) => row.splice(index, 0, row.pop()!));
229
- }
230
- return { data: new_data, headers: new_headers };
231
- };
232
-
233
- const update_array = (
234
- source: { id: string; value: CellValue }[][] | string[][] | null,
235
- target: any[] | null | undefined
236
- ): void => {
237
- if (source && target) {
238
- target.splice(0, target.length, ...JSON.parse(JSON.stringify(source)));
239
- }
240
- };
241
-
242
- return {
243
- handle_search: (query) =>
244
- update_state((s) => ({ current_search_query: query })),
245
- handle_sort: (col, direction) =>
246
- update_state((s) => {
247
- const sort_cols = s.sort_state.sort_columns.filter(
248
- (c) => c.col !== col
249
- );
250
- if (
251
- !s.sort_state.sort_columns.some(
252
- (c) => c.col === col && c.direction === direction
253
- )
254
- ) {
255
- sort_cols.push({ col, direction });
256
- }
257
-
258
- const initial_data =
259
- s.sort_state.initial_data ||
260
- (context.data && sort_cols.length > 0
261
- ? {
262
- data: JSON.parse(JSON.stringify(context.data)),
263
- display_value: context.display_value
264
- ? JSON.parse(JSON.stringify(context.display_value))
265
- : null,
266
- styling: context.styling
267
- ? JSON.parse(JSON.stringify(context.styling))
268
- : null
269
- }
270
- : null);
271
-
272
- return {
273
- sort_state: {
274
- ...s.sort_state,
275
- sort_columns: sort_cols.slice(-3),
276
- initial_data: initial_data
277
- }
278
- };
279
- }),
280
- handle_filter: (col, datatype, filter, value) =>
281
- update_state((s) => {
282
- const filter_cols = s.filter_state.filter_columns.some(
283
- (c) => c.col === col
284
- )
285
- ? s.filter_state.filter_columns.filter((c) => c.col !== col)
286
- : [
287
- ...s.filter_state.filter_columns,
288
- { col, datatype, filter, value }
289
- ];
290
-
291
- const initial_data =
292
- s.filter_state.initial_data ||
293
- (context.data && filter_cols.length > 0
294
- ? {
295
- data: JSON.parse(JSON.stringify(context.data)),
296
- display_value: context.display_value
297
- ? JSON.parse(JSON.stringify(context.display_value))
298
- : null,
299
- styling: context.styling
300
- ? JSON.parse(JSON.stringify(context.styling))
301
- : null
302
- }
303
- : null);
304
-
305
- return {
306
- filter_state: {
307
- ...s.filter_state,
308
- filter_columns: filter_cols,
309
- initial_data: initial_data
310
- }
311
- };
312
- }),
313
- get_sort_status: (name, headers) => {
314
- const s = get(state);
315
- const sort_item = s.sort_state.sort_columns.find(
316
- (item) => headers[item.col] === name
317
- );
318
- return sort_item ? sort_item.direction : "none";
319
- },
320
- sort_data: (data, display_value, styling) => {
321
- const {
322
- sort_state: { sort_columns }
323
- } = get(state);
324
- if (sort_columns.length)
325
- sort_table_data(data, display_value, styling, sort_columns);
326
- },
327
- update_row_order: (data) =>
328
- update_state((s) => ({
329
- sort_state: {
330
- ...s.sort_state,
331
- row_order:
332
- s.sort_state.sort_columns.length && data[0]
333
- ? [...Array(data.length)]
334
- .map((_, i) => i)
335
- .sort((a, b) => {
336
- for (const { col, direction } of s.sort_state
337
- .sort_columns) {
338
- const comp =
339
- (data[a]?.[col]?.value ?? "") <
340
- (data[b]?.[col]?.value ?? "")
341
- ? -1
342
- : 1;
343
- if (comp) return direction === "asc" ? comp : -comp;
344
- }
345
- return 0;
346
- })
347
- : [...Array(data.length)].map((_, i) => i)
348
- }
349
- })),
350
- filter_data: (data) => {
351
- const query = get(state).current_search_query?.toLowerCase();
352
- return query
353
- ? data.filter((row) =>
354
- row.some((cell) =>
355
- String(cell?.value).toLowerCase().includes(query)
356
- )
357
- )
358
- : data;
359
- },
360
- add_row,
361
- add_col,
362
- add_row_at: (data, index, position, make_id) =>
363
- add_row(data, make_id, position === "above" ? index : index + 1),
364
- add_col_at: (data, headers, index, position, make_id) =>
365
- add_col(data, headers, make_id, position === "left" ? index : index + 1),
366
- delete_row: (data, index) =>
367
- data.length > 1 ? data.filter((_, i) => i !== index) : data,
368
- delete_col: (data, headers, index) =>
369
- headers.length > 1
370
- ? {
371
- data: data.map((row) => row.filter((_, i) => i !== index)),
372
- headers: headers.filter((_, i) => i !== index)
373
- }
374
- : { data, headers },
375
- delete_row_at: (data, index) =>
376
- data.length > 1
377
- ? [...data.slice(0, index), ...data.slice(index + 1)]
378
- : data,
379
- delete_col_at: (data, headers, index) =>
380
- headers.length > 1
381
- ? {
382
- data: data.map((row) => [
383
- ...row.slice(0, index),
384
- ...row.slice(index + 1)
385
- ]),
386
- headers: [...headers.slice(0, index), ...headers.slice(index + 1)]
387
- }
388
- : { data, headers },
389
- trigger_change: async (
390
- data,
391
- headers,
392
- previous_data,
393
- previous_headers,
394
- value_is_output,
395
- dispatch
396
- ) => {
397
- const s = get(state);
398
- if (s.current_search_query) return;
399
-
400
- const current_headers = headers.map((h) => h.value);
401
- const current_data = data.map((row) => row.map((cell) => cell.value));
402
-
403
- if (
404
- !dequal(current_data, previous_data) ||
405
- !dequal(current_headers, previous_headers)
406
- ) {
407
- if (!dequal(current_headers, previous_headers)) {
408
- update_state((s) => ({
409
- sort_state: { sort_columns: [], row_order: [], initial_data: null },
410
- filter_state: { filter_columns: [], initial_data: null }
411
- }));
412
- }
413
- dispatch("change", {
414
- data: data.map((row) => row.map((cell) => cell.value)),
415
- headers: current_headers,
416
- metadata: null
417
- });
418
- const index = s.ui_state.selected;
419
- if (index) {
420
- dispatch("edit", {
421
- index,
422
- value: data[index[0]][index[1]].value,
423
- previous_value: previous_data[index[0]][index[1]]
424
- });
425
- }
426
- if (!value_is_output) dispatch("input");
427
- }
428
- },
429
- reset_sort_state: () =>
430
- update_state((s) => {
431
- if (s.sort_state.initial_data && context.data) {
432
- const original = s.sort_state.initial_data;
433
-
434
- update_array(original.data, context.data);
435
- update_array(original.display_value, context.display_value);
436
- update_array(original.styling, context.styling);
437
- }
438
-
439
- return {
440
- sort_state: { sort_columns: [], row_order: [], initial_data: null }
441
- };
442
- }),
443
- reset_filter_state: () =>
444
- update_state((s) => {
445
- if (s.filter_state.initial_data && context.data) {
446
- const original = s.filter_state.initial_data;
447
-
448
- update_array(original.data, context.data);
449
- update_array(original.display_value, context.display_value);
450
- update_array(original.styling, context.styling);
451
- }
452
-
453
- return {
454
- filter_state: { filter_columns: [], initial_data: null }
455
- };
456
- }),
457
- set_active_cell_menu: (menu) =>
458
- update_state((s) => ({
459
- ui_state: { ...s.ui_state, active_cell_menu: menu }
460
- })),
461
- set_active_header_menu: (menu) =>
462
- update_state((s) => ({
463
- ui_state: { ...s.ui_state, active_header_menu: menu }
464
- })),
465
- set_selected_cells: (cells) =>
466
- update_state((s) => ({
467
- ui_state: { ...s.ui_state, selected_cells: cells }
468
- })),
469
- set_selected: (selected) =>
470
- update_state((s) => ({ ui_state: { ...s.ui_state, selected } })),
471
- set_editing: (editing) =>
472
- update_state((s) => ({ ui_state: { ...s.ui_state, editing } })),
473
- clear_ui_state: () =>
474
- update_state((s) => ({
475
- ui_state: {
476
- active_cell_menu: null,
477
- active_header_menu: null,
478
- selected_cells: [],
479
- selected: false,
480
- editing: false,
481
- header_edit: false,
482
- selected_header: false,
483
- active_button: null,
484
- copy_flash: false
485
- }
486
- })),
487
- set_header_edit: (header_index) =>
488
- update_state((s) => ({
489
- ui_state: {
490
- ...s.ui_state,
491
- selected_cells: [],
492
- selected_header: header_index,
493
- header_edit: header_index
494
- }
495
- })),
496
- set_selected_header: (header_index) =>
497
- update_state((s) => ({
498
- ui_state: {
499
- ...s.ui_state,
500
- selected_header: header_index,
501
- selected: false,
502
- selected_cells: []
503
- }
504
- })),
505
- handle_header_click: (col, editable) =>
506
- update_state((s) => ({
507
- ui_state: {
508
- ...s.ui_state,
509
- active_cell_menu: null,
510
- active_header_menu: null,
511
- selected: false,
512
- selected_cells: [],
513
- selected_header: col,
514
- header_edit: editable ? col : false
515
- }
516
- })),
517
- end_header_edit: (key) => {
518
- if (["Escape", "Enter", "Tab"].includes(key)) {
519
- update_state((s) => ({
520
- ui_state: { ...s.ui_state, selected: false, header_edit: false }
521
- }));
522
- }
523
- },
524
- get_selected_cells: () => get(state).ui_state.selected_cells,
525
- get_active_cell_menu: () => get(state).ui_state.active_cell_menu,
526
- get_active_button: () => get(state).ui_state.active_button,
527
- set_active_button: (button) =>
528
- update_state((s) => ({
529
- ui_state: { ...s.ui_state, active_button: button }
530
- })),
531
- set_copy_flash: (value) =>
532
- update_state((s) => ({ ui_state: { ...s.ui_state, copy_flash: value } })),
533
- handle_cell_click: (event, row, col) => {
534
- event.preventDefault();
535
- event.stopPropagation();
536
-
537
- const s = get(state);
538
- if (s.config.show_row_numbers && col === -1) return;
539
-
540
- let actual_row = row;
541
- if (s.current_search_query && context.data) {
542
- const filtered_indices: number[] = [];
543
- context.data.forEach((dataRow, idx) => {
544
- if (
545
- dataRow.some((cell) =>
546
- String(cell?.value)
547
- .toLowerCase()
548
- .includes(s.current_search_query?.toLowerCase() || "")
549
- )
550
- ) {
551
- filtered_indices.push(idx);
552
- }
553
- });
554
- actual_row = filtered_indices[row] ?? row;
555
- }
556
-
557
- const cells = handle_selection(
558
- [actual_row, col],
559
- s.ui_state.selected_cells,
560
- event
561
- );
562
- update_state((s) => ({
563
- ui_state: {
564
- ...s.ui_state,
565
- active_cell_menu: null,
566
- active_header_menu: null,
567
- selected_header: false,
568
- header_edit: false,
569
- selected_cells: cells,
570
- selected: cells[0]
571
- }
572
- }));
573
-
574
- if (s.config.editable && cells.length === 1) {
575
- update_state((s) => ({
576
- ui_state: { ...s.ui_state, editing: [actual_row, col] }
577
- }));
578
- tick().then(() =>
579
- context.els![context.data![actual_row][col].id]?.input?.focus()
580
- );
581
- } else {
582
- // ensure parent has focus for keyboard navigation
583
- tick().then(() => {
584
- if (context.parent_element) {
585
- context.parent_element.focus();
586
- }
587
- });
588
- }
589
-
590
- context.dispatch?.("select", {
591
- index: [actual_row, col],
592
- col_value: context.get_column!(col),
593
- row_value: context.get_row!(actual_row),
594
- value: context.get_data_at!(actual_row, col)
595
- });
596
- },
597
- toggle_cell_menu: (event, row, col) => {
598
- event.stopPropagation();
599
- const current_menu = get(state).ui_state.active_cell_menu;
600
- if (current_menu?.row === row && current_menu.col === col) {
601
- update_state((s) => ({
602
- ui_state: { ...s.ui_state, active_cell_menu: null }
603
- }));
604
- } else {
605
- const cell = (event.target as HTMLElement).closest("td");
606
- if (cell) {
607
- const rect = cell.getBoundingClientRect();
608
- update_state((s) => ({
609
- ui_state: {
610
- ...s.ui_state,
611
- active_cell_menu: { row, col, x: rect.right, y: rect.bottom }
612
- }
613
- }));
614
- }
615
- }
616
- },
617
- toggle_cell_button: (row, col) => {
618
- const current_button = get(state).ui_state.active_button;
619
- const new_button =
620
- current_button?.type === "cell" &&
621
- current_button.row === row &&
622
- current_button.col === col
623
- ? null
624
- : { type: "cell" as const, row, col };
625
- update_state((s) => ({
626
- ui_state: { ...s.ui_state, active_button: new_button }
627
- }));
628
- },
629
- handle_select_column: (col) => {
630
- if (!context.data) return;
631
- const cells = context.data.map((_, row) => [row, col] as CellCoordinate);
632
- update_state((s) => ({
633
- ui_state: {
634
- ...s.ui_state,
635
- selected_cells: cells,
636
- selected: cells[0],
637
- editing: false
638
- }
639
- }));
640
- setTimeout(() => context.parent_element?.focus(), 0);
641
- },
642
- handle_select_row: (row) => {
643
- if (!context.data || !context.data[0]) return;
644
- const cells = context.data[0].map(
645
- (_, col) => [row, col] as CellCoordinate
646
- );
647
- update_state((s) => ({
648
- ui_state: {
649
- ...s.ui_state,
650
- selected_cells: cells,
651
- selected: cells[0],
652
- editing: false
653
- }
654
- }));
655
- setTimeout(() => context.parent_element?.focus(), 0);
656
- },
657
- get_next_cell_coordinates,
658
- get_range_selection,
659
- move_cursor
660
- };
661
- }
662
-
663
- export function create_dataframe_context(
664
- config: DataFrameState["config"]
665
- ): DataFrameContext {
666
- const state = writable<DataFrameState>({
667
- config,
668
- current_search_query: null,
669
- sort_state: { sort_columns: [], row_order: [], initial_data: null },
670
- filter_state: { filter_columns: [], initial_data: null },
671
- ui_state: {
672
- active_cell_menu: null,
673
- active_header_menu: null,
674
- selected_cells: [],
675
- selected: false,
676
- editing: false,
677
- header_edit: false,
678
- selected_header: false,
679
- active_button: null,
680
- copy_flash: false
681
- }
682
- });
683
-
684
- const context: DataFrameContext = { state, actions: null as any };
685
- context.actions = create_actions(state, context);
686
-
687
- const instance_id = Symbol(
688
- `dataframe_${Math.random().toString(36).substring(2)}`
689
- );
690
- setContext(instance_id, context);
691
- setContext(DATAFRAME_KEY, { instance_id, context });
692
-
693
- return context;
694
- }
695
-
696
- export function get_dataframe_context(): DataFrameContext {
697
- const ctx = getContext<{ instance_id: symbol; context: DataFrameContext }>(
698
- DATAFRAME_KEY
699
- );
700
- return ctx?.context ?? getContext<DataFrameContext>(DATAFRAME_KEY);
701
- }
@@ -1,25 +0,0 @@
1
- <script lang="ts">
2
- export let size = 16;
3
- </script>
4
-
5
- <svg
6
- width={size}
7
- height={size}
8
- viewBox="0 0 16 16"
9
- fill="none"
10
- xmlns="http://www.w3.org/2000/svg"
11
- >
12
- <path
13
- d="M4 8L8 12L12 8"
14
- stroke="currentColor"
15
- stroke-width="1.5"
16
- stroke-linecap="round"
17
- stroke-linejoin="round"
18
- />
19
- <path
20
- d="M8 12V4"
21
- stroke="currentColor"
22
- stroke-width="1.5"
23
- stroke-linecap="round"
24
- />
25
- </svg>