@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
@@ -0,0 +1,625 @@
1
+ import { getContext, setContext } from "svelte";
2
+ import { writable, get } from "svelte/store";
3
+ import type { Writable } from "svelte/store";
4
+ import { sort_table_data } from "../utils/table_utils";
5
+ import { dequal } from "dequal/lite";
6
+ import type { DataframeValue } from "../utils";
7
+
8
+ export const DATAFRAME_KEY = Symbol("dataframe");
9
+
10
+ export type SortDirection = "asc" | "desc";
11
+
12
+ export type DataFrameState = {
13
+ config: {
14
+ show_fullscreen_button: boolean;
15
+ show_copy_button: boolean;
16
+ show_search: "none" | "search" | "filter";
17
+ show_row_numbers: boolean;
18
+ editable: boolean;
19
+ pinned_columns: number;
20
+ show_label: boolean;
21
+ line_breaks: boolean;
22
+ wrap: boolean;
23
+ max_height: number;
24
+ column_widths: string[];
25
+ max_chars: number | undefined;
26
+ };
27
+ current_search_query: string | null;
28
+ sort_state: {
29
+ sort_columns: { col: number; direction: SortDirection }[];
30
+ row_order: number[];
31
+ };
32
+ ui_state: {
33
+ active_cell_menu: { row: number; col: number; x: number; y: number } | null;
34
+ active_header_menu: { col: number; x: number; y: number } | null;
35
+ selected_cells: [number, number][];
36
+ selected: [number, number] | false;
37
+ editing: [number, number] | false;
38
+ header_edit: number | false;
39
+ selected_header: number | false;
40
+ active_button: {
41
+ type: "header" | "cell";
42
+ row?: number;
43
+ col: number;
44
+ } | null;
45
+ };
46
+ };
47
+
48
+ export interface DataFrameContext {
49
+ state: Writable<DataFrameState>;
50
+ actions: {
51
+ handle_search: (search_query: string | null) => void;
52
+ handle_sort: (col: number, direction: SortDirection) => void;
53
+ get_sort_status: (
54
+ name: string,
55
+ headers: string[]
56
+ ) => "none" | "asc" | "desc";
57
+ sort_data: (
58
+ data: any[][],
59
+ display_value: string[][] | null,
60
+ styling: string[][] | null
61
+ ) => void;
62
+ update_row_order: (data: any[][]) => void;
63
+ filter_data: (data: any[][]) => any[][];
64
+ add_row: (data: any[][], make_id: () => string, index?: number) => any[][];
65
+ add_col: (
66
+ data: any[][],
67
+ headers: string[],
68
+ make_id: () => string,
69
+ index?: number
70
+ ) => { data: any[][]; headers: string[] };
71
+ add_row_at: (
72
+ data: any[][],
73
+ index: number,
74
+ position: "above" | "below",
75
+ make_id: () => string
76
+ ) => any[][];
77
+ add_col_at: (
78
+ data: any[][],
79
+ headers: string[],
80
+ index: number,
81
+ position: "left" | "right",
82
+ make_id: () => string
83
+ ) => { data: any[][]; headers: string[] };
84
+ delete_row: (data: any[][], index: number) => any[][];
85
+ delete_col: (
86
+ data: any[][],
87
+ headers: string[],
88
+ index: number
89
+ ) => { data: any[][]; headers: string[] };
90
+ delete_row_at: (data: any[][], index: number) => any[][];
91
+ delete_col_at: (
92
+ data: any[][],
93
+ headers: string[],
94
+ index: number
95
+ ) => { data: any[][]; headers: string[] };
96
+ set_active_cell_menu: (
97
+ menu: { row: number; col: number; x: number; y: number } | null
98
+ ) => void;
99
+ set_active_header_menu: (
100
+ menu: { col: number; x: number; y: number } | null
101
+ ) => void;
102
+ set_selected_cells: (cells: [number, number][]) => void;
103
+ set_selected: (selected: [number, number] | false) => void;
104
+ set_editing: (editing: [number, number] | false) => void;
105
+ clear_ui_state: () => void;
106
+ set_header_edit: (header_index: number | false) => void;
107
+ set_selected_header: (header_index: number | false) => void;
108
+ handle_header_click: (col: number, editable: boolean) => void;
109
+ end_header_edit: (key: string) => void;
110
+ trigger_change: (
111
+ data: any[][],
112
+ headers: any[],
113
+ previous_data: string[][],
114
+ previous_headers: string[],
115
+ value_is_output: boolean,
116
+ dispatch: {
117
+ (e: "change", detail: DataframeValue): void;
118
+ (e: "input", detail?: undefined): void;
119
+ (e: "select", detail: any): void;
120
+ (e: "search", detail: string | null): void;
121
+ }
122
+ ) => Promise<void>;
123
+ get_selected_cells: () => [number, number][];
124
+ get_active_cell_menu: () => {
125
+ row: number;
126
+ col: number;
127
+ x: number;
128
+ y: number;
129
+ } | null;
130
+ get_active_button: () => {
131
+ type: "header" | "cell";
132
+ row?: number;
133
+ col: number;
134
+ } | null;
135
+ set_active_button: (
136
+ button: { type: "header" | "cell"; row?: number; col: number } | null
137
+ ) => void;
138
+ reset_sort_state: () => void;
139
+ };
140
+ }
141
+
142
+ export function create_actions(
143
+ state: Writable<DataFrameState>
144
+ ): DataFrameContext["actions"] {
145
+ const add_row = (
146
+ data: any[][],
147
+ make_id: () => string,
148
+ index?: number
149
+ ): any[][] => {
150
+ if (!data || data.length === 0) {
151
+ return [[{ value: "", id: make_id() }]];
152
+ }
153
+
154
+ const new_row = Array(data[0].length)
155
+ .fill(null)
156
+ .map(() => ({
157
+ value: "",
158
+ id: make_id()
159
+ }));
160
+
161
+ const new_data = [...data];
162
+ if (typeof index === "number" && index >= 0 && index <= data.length) {
163
+ new_data.splice(index, 0, new_row);
164
+ } else {
165
+ new_data.push(new_row);
166
+ }
167
+
168
+ return new_data;
169
+ };
170
+
171
+ const add_col = (
172
+ data: any[][],
173
+ headers: string[],
174
+ make_id: () => string,
175
+ index?: number
176
+ ): { data: any[][]; headers: string[] } => {
177
+ if (!data || data.length === 0) {
178
+ return {
179
+ data: [[{ value: "", id: make_id() }]],
180
+ headers: ["Header 1"]
181
+ };
182
+ }
183
+
184
+ const new_headers = [...headers];
185
+ const new_data = data.map((row) => [...row]);
186
+
187
+ if (
188
+ typeof index === "number" &&
189
+ index >= 0 &&
190
+ index <= (data[0]?.length || 0)
191
+ ) {
192
+ new_headers.splice(index, 0, `Header ${headers.length + 1}`);
193
+ new_data.forEach((row) => {
194
+ const id = make_id();
195
+ row.splice(index, 0, { value: "", id });
196
+ });
197
+ } else {
198
+ new_headers.push(`Header ${headers.length + 1}`);
199
+ new_data.forEach((row) => {
200
+ const id = make_id();
201
+ row.push({ value: "", id });
202
+ });
203
+ }
204
+
205
+ return { data: new_data, headers: new_headers };
206
+ };
207
+
208
+ const reset_sort_state = (): void => {
209
+ state.update((s) => ({
210
+ ...s,
211
+ sort_state: {
212
+ sort_columns: [],
213
+ row_order: []
214
+ }
215
+ }));
216
+ };
217
+
218
+ return {
219
+ handle_search: (search_query: string | null) => {
220
+ state.update((s) => ({ ...s, current_search_query: search_query }));
221
+ },
222
+ handle_sort: (col: number, direction: SortDirection) => {
223
+ state.update((s) => {
224
+ const sort_columns = [...s.sort_state.sort_columns];
225
+ const existing_index = sort_columns.findIndex(
226
+ (item) => item.col === col
227
+ );
228
+
229
+ if (existing_index !== -1) {
230
+ const existing_item = sort_columns[existing_index];
231
+
232
+ if (existing_item.direction === direction) {
233
+ sort_columns.splice(existing_index, 1);
234
+ } else {
235
+ sort_columns[existing_index] = { col, direction };
236
+ }
237
+ } else {
238
+ if (sort_columns.length >= 3) {
239
+ sort_columns.shift();
240
+ }
241
+ sort_columns.push({ col, direction });
242
+ }
243
+
244
+ return {
245
+ ...s,
246
+ sort_state: {
247
+ ...s.sort_state,
248
+ sort_columns
249
+ }
250
+ };
251
+ });
252
+ },
253
+ get_sort_status: (
254
+ name: string,
255
+ headers: string[]
256
+ ): "none" | "asc" | "desc" => {
257
+ const current_state = get(state);
258
+ const sort_item = current_state.sort_state.sort_columns.find(
259
+ (item) => headers[item.col] === name
260
+ );
261
+
262
+ if (!sort_item) return "none";
263
+ return sort_item.direction;
264
+ },
265
+ sort_data: (
266
+ data: any[][],
267
+ display_value: string[][] | null,
268
+ styling: string[][] | null
269
+ ) => {
270
+ const current_state = get(state);
271
+ if (current_state.sort_state.sort_columns.length > 0) {
272
+ sort_table_data(
273
+ data,
274
+ display_value,
275
+ styling,
276
+ current_state.sort_state.sort_columns
277
+ );
278
+ }
279
+ },
280
+ update_row_order: (data: any[][]) => {
281
+ state.update((s) => {
282
+ const current_sort_state = { ...s.sort_state };
283
+ if (current_sort_state.sort_columns.length > 0 && data[0]) {
284
+ const indices = [...Array(data.length)].map((_, i) => i);
285
+ indices.sort((a, b) => {
286
+ const row_a = data[a];
287
+ const row_b = data[b];
288
+
289
+ for (const {
290
+ col: sort_index,
291
+ direction
292
+ } of current_sort_state.sort_columns) {
293
+ if (
294
+ !row_a ||
295
+ !row_b ||
296
+ sort_index < 0 ||
297
+ sort_index >= row_a.length ||
298
+ sort_index >= row_b.length
299
+ ) {
300
+ continue;
301
+ }
302
+
303
+ const val_a = row_a[sort_index].value;
304
+ const val_b = row_b[sort_index].value;
305
+ const comp = val_a < val_b ? -1 : val_a > val_b ? 1 : 0;
306
+
307
+ if (comp !== 0) {
308
+ return direction === "asc" ? comp : -comp;
309
+ }
310
+ }
311
+
312
+ return 0;
313
+ });
314
+ current_sort_state.row_order = indices;
315
+ } else {
316
+ current_sort_state.row_order = [...Array(data.length)].map(
317
+ (_, i) => i
318
+ );
319
+ }
320
+ return { ...s, sort_state: current_sort_state };
321
+ });
322
+ },
323
+ filter_data: (data: any[][]) => {
324
+ const current_state = get(state);
325
+
326
+ if (!current_state.current_search_query) {
327
+ return data;
328
+ }
329
+
330
+ const search_query = current_state.current_search_query.toLowerCase();
331
+
332
+ const filtered = data.filter((row) => {
333
+ return row.some((cell) => {
334
+ if (!cell) {
335
+ return false;
336
+ }
337
+
338
+ const cell_value = cell.value;
339
+
340
+ if (cell_value === null || cell_value === undefined) {
341
+ return false;
342
+ }
343
+
344
+ const string_value = String(cell_value).toLowerCase();
345
+ return string_value.includes(search_query);
346
+ });
347
+ });
348
+
349
+ return filtered;
350
+ },
351
+ add_row,
352
+ add_col,
353
+ add_row_at: (
354
+ data: any[][],
355
+ index: number,
356
+ position: "above" | "below",
357
+ make_id: () => string
358
+ ) => {
359
+ const row_index = position === "above" ? index : index + 1;
360
+ return add_row(data, make_id, row_index);
361
+ },
362
+ add_col_at: (
363
+ data: any[][],
364
+ headers: string[],
365
+ index: number,
366
+ position: "left" | "right",
367
+ make_id: () => string
368
+ ) => {
369
+ const col_index = position === "left" ? index : index + 1;
370
+ return add_col(data, headers, make_id, col_index);
371
+ },
372
+ delete_row: (data: any[][], index: number) => {
373
+ if (data.length <= 1) {
374
+ return data;
375
+ }
376
+
377
+ const new_data = [...data];
378
+ new_data.splice(index, 1);
379
+
380
+ return new_data;
381
+ },
382
+ delete_col: (
383
+ data: any[][],
384
+ headers: string[],
385
+ index: number
386
+ ): { data: any[][]; headers: string[] } => {
387
+ if (headers.length <= 1) {
388
+ return { data, headers };
389
+ }
390
+
391
+ const new_headers = [...headers];
392
+ new_headers.splice(index, 1);
393
+
394
+ const new_data = data.map((row) => {
395
+ const new_row = [...row];
396
+ new_row.splice(index, 1);
397
+ return new_row;
398
+ });
399
+
400
+ return { data: new_data, headers: new_headers };
401
+ },
402
+ delete_row_at: (data: any[][], index: number) => {
403
+ if (data.length <= 1) return data;
404
+ data.splice(index, 1);
405
+ return data;
406
+ },
407
+ delete_col_at: (data: any[][], headers: string[], index: number) => {
408
+ if (headers.length <= 1) {
409
+ return { data, headers };
410
+ }
411
+
412
+ const new_headers = [...headers];
413
+ new_headers.splice(index, 1);
414
+
415
+ const new_data = data.map((row) => {
416
+ const new_row = [...row];
417
+ new_row.splice(index, 1);
418
+ return new_row;
419
+ });
420
+
421
+ return { data: new_data, headers: new_headers };
422
+ },
423
+ set_active_cell_menu: (menu) => {
424
+ state.update((s) => ({
425
+ ...s,
426
+ ui_state: { ...s.ui_state, active_cell_menu: menu }
427
+ }));
428
+ },
429
+ set_active_header_menu: (menu) => {
430
+ state.update((s) => ({
431
+ ...s,
432
+ ui_state: { ...s.ui_state, active_header_menu: menu }
433
+ }));
434
+ },
435
+ set_selected_cells: (cells) => {
436
+ state.update((s) => ({
437
+ ...s,
438
+ ui_state: { ...s.ui_state, selected_cells: cells }
439
+ }));
440
+ },
441
+ set_selected: (selected) => {
442
+ state.update((s) => ({
443
+ ...s,
444
+ ui_state: { ...s.ui_state, selected: selected }
445
+ }));
446
+ },
447
+ set_editing: (editing) => {
448
+ state.update((s) => ({
449
+ ...s,
450
+ ui_state: { ...s.ui_state, editing: editing }
451
+ }));
452
+ },
453
+ clear_ui_state: () => {
454
+ state.update((s) => ({
455
+ ...s,
456
+ ui_state: {
457
+ active_cell_menu: null,
458
+ active_header_menu: null,
459
+ selected_cells: [],
460
+ selected: false,
461
+ editing: false,
462
+ header_edit: false,
463
+ selected_header: false,
464
+ active_button: null
465
+ }
466
+ }));
467
+ },
468
+ set_header_edit: (header_index: number | false) => {
469
+ state.update((s) => ({
470
+ ...s,
471
+ ui_state: {
472
+ ...s.ui_state,
473
+ selected_cells: [],
474
+ selected_header: header_index,
475
+ header_edit: header_index
476
+ }
477
+ }));
478
+ },
479
+ set_selected_header: (header_index: number | false) => {
480
+ state.update((s) => ({
481
+ ...s,
482
+ ui_state: {
483
+ ...s.ui_state,
484
+ selected_header: header_index,
485
+ selected: false,
486
+ selected_cells: []
487
+ }
488
+ }));
489
+ },
490
+ handle_header_click: (col: number, editable: boolean) => {
491
+ state.update((s) => ({
492
+ ...s,
493
+ ui_state: {
494
+ ...s.ui_state,
495
+ active_cell_menu: null,
496
+ active_header_menu: null,
497
+ selected: false,
498
+ selected_cells: [],
499
+ selected_header: col,
500
+ header_edit: editable ? col : false
501
+ }
502
+ }));
503
+ },
504
+ end_header_edit: (key: string) => {
505
+ if (key === "Escape" || key === "Enter" || key === "Tab") {
506
+ state.update((s) => ({
507
+ ...s,
508
+ ui_state: {
509
+ ...s.ui_state,
510
+ selected: false,
511
+ header_edit: false
512
+ }
513
+ }));
514
+ }
515
+ },
516
+ trigger_change: async (
517
+ data: any[][],
518
+ headers: any[],
519
+ previous_data: string[][],
520
+ previous_headers: string[],
521
+ value_is_output: boolean,
522
+ dispatch: {
523
+ (e: "change", detail: DataframeValue): void;
524
+ (e: "input", detail?: undefined): void;
525
+ (e: "select", detail: any): void;
526
+ (e: "search", detail: string | null): void;
527
+ }
528
+ ): Promise<void> => {
529
+ const current_state = get(state);
530
+ if (current_state.current_search_query) return;
531
+
532
+ const current_headers = headers.map((h) => h.value);
533
+ const current_data = data.map((row) =>
534
+ row.map((cell) => String(cell.value))
535
+ );
536
+
537
+ if (
538
+ !dequal(current_data, previous_data) ||
539
+ !dequal(current_headers, previous_headers)
540
+ ) {
541
+ if (!dequal(current_headers, previous_headers)) {
542
+ reset_sort_state();
543
+ }
544
+
545
+ dispatch("change", {
546
+ data: data.map((row) => row.map((cell) => cell.value)),
547
+ headers: headers.map((h) => h.value),
548
+ metadata: null
549
+ });
550
+ if (!value_is_output) {
551
+ dispatch("input");
552
+ }
553
+ }
554
+ },
555
+ get_selected_cells: () => {
556
+ const current_state = get(state);
557
+ return current_state.ui_state.selected_cells;
558
+ },
559
+ get_active_cell_menu: () => {
560
+ const current_state = get(state);
561
+ return current_state.ui_state.active_cell_menu;
562
+ },
563
+ get_active_button: () => {
564
+ const current_state = get(state);
565
+ return current_state.ui_state.active_button;
566
+ },
567
+ set_active_button: (button) => {
568
+ state.update((s) => ({
569
+ ...s,
570
+ ui_state: { ...s.ui_state, active_button: button }
571
+ }));
572
+ },
573
+ reset_sort_state
574
+ };
575
+ }
576
+
577
+ export function create_dataframe_context(config: {
578
+ show_fullscreen_button: boolean;
579
+ show_copy_button: boolean;
580
+ show_search: "none" | "search" | "filter";
581
+ show_row_numbers: boolean;
582
+ editable: boolean;
583
+ pinned_columns: number;
584
+ show_label: boolean;
585
+ line_breaks: boolean;
586
+ wrap: boolean;
587
+ max_height: number;
588
+ column_widths: string[];
589
+ max_chars: number | undefined;
590
+ }): DataFrameContext {
591
+ const instance_id = Symbol(
592
+ `dataframe_${Math.random().toString(36).substring(2)}`
593
+ );
594
+ const state = writable<DataFrameState>({
595
+ config,
596
+ current_search_query: null,
597
+ sort_state: {
598
+ sort_columns: [],
599
+ row_order: []
600
+ },
601
+ ui_state: {
602
+ active_cell_menu: null,
603
+ active_header_menu: null,
604
+ selected_cells: [],
605
+ selected: false,
606
+ editing: false,
607
+ header_edit: false,
608
+ selected_header: false,
609
+ active_button: null
610
+ }
611
+ });
612
+
613
+ const actions = create_actions(state);
614
+ const context: DataFrameContext = { state, actions };
615
+ setContext(instance_id, context);
616
+ setContext(DATAFRAME_KEY, { instance_id, context });
617
+ return context;
618
+ }
619
+
620
+ export function get_dataframe_context(): DataFrameContext {
621
+ const ctx = getContext<{ instance_id: symbol; context: DataFrameContext }>(
622
+ DATAFRAME_KEY
623
+ );
624
+ return ctx ? ctx.context : getContext<DataFrameContext>(DATAFRAME_KEY);
625
+ }
@@ -0,0 +1,24 @@
1
+ <div class="wrapper" aria-label="Static column">
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="13"
5
+ height="13"
6
+ viewBox="0 0 24 24"
7
+ fill="none"
8
+ stroke="currentColor"
9
+ stroke-width="2"
10
+ stroke-linecap="round"
11
+ stroke-linejoin="round"
12
+ >
13
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
14
+ <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
15
+ </svg>
16
+ </div>
17
+
18
+ <style>
19
+ .wrapper {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ }
24
+ </style>