@gradio/dataframe 0.19.3 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/Index.svelte +2 -0
  3. package/dist/Index.svelte +1 -0
  4. package/dist/Index.svelte.d.ts +3 -0
  5. package/dist/shared/BooleanCell.svelte +1 -2
  6. package/dist/shared/BooleanCell.svelte.d.ts +1 -1
  7. package/dist/shared/EditableCell.svelte +7 -18
  8. package/dist/shared/EditableCell.svelte.d.ts +2 -1
  9. package/dist/shared/Table.svelte +5 -13
  10. package/dist/shared/Table.svelte.d.ts +4 -2
  11. package/dist/shared/TableCell.svelte.d.ts +2 -1
  12. package/dist/shared/context/dataframe_context.d.ts +9 -8
  13. package/dist/shared/context/dataframe_context.js +9 -1
  14. package/dist/shared/types.d.ts +2 -1
  15. package/dist/shared/utils/data_processing.d.ts +5 -4
  16. package/dist/shared/utils/data_processing.js +4 -2
  17. package/dist/shared/utils/filter_utils.d.ts +5 -4
  18. package/dist/shared/utils/keyboard_utils.js +1 -1
  19. package/dist/shared/utils/selection_utils.d.ts +3 -3
  20. package/dist/shared/utils/sort_utils.d.ts +4 -4
  21. package/dist/shared/utils/table_utils.d.ts +2 -2
  22. package/dist/shared/utils/utils.d.ts +8 -2
  23. package/dist/standalone/Index.svelte +2 -2
  24. package/dist/standalone/Index.svelte.d.ts +1 -1
  25. package/package.json +5 -5
  26. package/shared/BooleanCell.svelte +2 -5
  27. package/shared/EditableCell.svelte +12 -20
  28. package/shared/Table.svelte +24 -27
  29. package/shared/TableCell.svelte +2 -1
  30. package/shared/context/dataframe_context.ts +20 -13
  31. package/shared/types.ts +3 -1
  32. package/shared/utils/data_processing.ts +10 -5
  33. package/shared/utils/filter_utils.ts +5 -4
  34. package/shared/utils/keyboard_utils.ts +1 -6
  35. package/shared/utils/selection_utils.ts +3 -3
  36. package/shared/utils/sort_utils.ts +4 -4
  37. package/shared/utils/table_utils.ts +8 -2
  38. package/shared/utils/utils.ts +10 -5
  39. package/standalone/Index.svelte +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @gradio/dataframe
2
2
 
3
+ ## 0.20.0
4
+
5
+ ### Features
6
+
7
+ - [#11959](https://github.com/gradio-app/gradio/pull/11959) [`cb16ca5`](https://github.com/gradio-app/gradio/commit/cb16ca5123030beebcb24ddc287fcf958bd3fbe2) - Ensure interactivity works as expected in standalone dataframe. Thanks @hannahblair!
8
+
9
+ ## 0.19.4
10
+
11
+ ### Fixes
12
+
13
+ - [#11648](https://github.com/gradio-app/gradio/pull/11648) [`57d8d65`](https://github.com/gradio-app/gradio/commit/57d8d6598645e438337c78c1c8e0759f876fb193) - Dataframe Edit Event. Thanks @deckar01!
14
+
15
+ ### Dependency updates
16
+
17
+ - @gradio/markdown-code@0.5.2
18
+
3
19
  ## 0.19.3
4
20
 
5
21
  ### Features
package/Index.svelte CHANGED
@@ -41,6 +41,7 @@
41
41
  input: never;
42
42
  clear_status: LoadingStatus;
43
43
  search: string | null;
44
+ edit: SelectData;
44
45
  }>;
45
46
  export let latex_delimiters: {
46
47
  left: string;
@@ -95,6 +96,7 @@
95
96
  }}
96
97
  on:input={(e) => gradio.dispatch("input")}
97
98
  on:select={(e) => gradio.dispatch("select", e.detail)}
99
+ on:edit={(e) => gradio.dispatch("edit", e.detail)}
98
100
  on:fullscreen={({ detail }) => {
99
101
  fullscreen = detail;
100
102
  }}
package/dist/Index.svelte CHANGED
@@ -78,6 +78,7 @@ export let fullscreen = false;
78
78
  }}
79
79
  on:input={(e) => gradio.dispatch("input")}
80
80
  on:select={(e) => gradio.dispatch("select", e.detail)}
81
+ on:edit={(e) => gradio.dispatch("edit", e.detail)}
81
82
  on:fullscreen={({ detail }) => {
82
83
  fullscreen = detail;
83
84
  }}
@@ -28,6 +28,7 @@ declare const __propDef: {
28
28
  input: never;
29
29
  clear_status: LoadingStatus;
30
30
  search: string | null;
31
+ edit: SelectData;
31
32
  }>;
32
33
  latex_delimiters: {
33
34
  left: string;
@@ -111,6 +112,7 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
111
112
  input: never;
112
113
  clear_status: LoadingStatus;
113
114
  search: string | null;
115
+ edit: SelectData;
114
116
  }>;
115
117
  /**accessor*/
116
118
  set gradio(_: Gradio<{
@@ -119,6 +121,7 @@ export default class Index extends SvelteComponent<IndexProps, IndexEvents, Inde
119
121
  input: never;
120
122
  clear_status: LoadingStatus;
121
123
  search: string | null;
124
+ edit: SelectData;
122
125
  }>);
123
126
  get latex_delimiters(): {
124
127
  left: string;
@@ -2,7 +2,6 @@
2
2
  export let value = false;
3
3
  export let editable = true;
4
4
  export let on_change;
5
- $: bool_value = typeof value === "string" ? value.toLowerCase() === "true" : !!value;
6
5
  function handle_change(event) {
7
6
  if (editable) {
8
7
  on_change(event.detail);
@@ -12,7 +11,7 @@ function handle_change(event) {
12
11
 
13
12
  <div class="bool-cell" role="button" tabindex="-1">
14
13
  <BaseCheckbox
15
- bind:value={bool_value}
14
+ bind:value
16
15
  label=""
17
16
  interactive={editable}
18
17
  on:change={handle_change}
@@ -1,7 +1,7 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
- value?: boolean | string;
4
+ value?: boolean;
5
5
  editable?: boolean;
6
6
  on_change: (value: boolean) => void;
7
7
  };
@@ -48,18 +48,11 @@ function handle_blur(event) {
48
48
  function handle_keydown(event) {
49
49
  dispatch("keydown", event);
50
50
  }
51
- function handle_bool_change(new_value) {
52
- value = new_value.toString();
53
- dispatch("blur", {
54
- blur_event: {
55
- target: {
56
- type: "checkbox",
57
- checked: new_value,
58
- value: new_value.toString()
59
- }
60
- },
61
- coords
62
- });
51
+ function commit_change(checked) {
52
+ handle_blur({ target: { value } });
53
+ }
54
+ $: if (!edit) {
55
+ handle_blur({ target: { value } });
63
56
  }
64
57
  </script>
65
58
 
@@ -80,12 +73,8 @@ function handle_bool_change(new_value) {
80
73
  />
81
74
  {/if}
82
75
 
83
- {#if datatype === "bool"}
84
- <BooleanCell
85
- value={String(display_content)}
86
- {editable}
87
- on_change={handle_bool_change}
88
- />
76
+ {#if datatype === "bool" && typeof value === "boolean"}
77
+ <BooleanCell bind:value {editable} on_change={commit_change} />
89
78
  {:else}
90
79
  <span
91
80
  class:dragging={is_dragging}
@@ -1,9 +1,10 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import type { I18nFormatter } from "@gradio/utils";
3
+ import type { CellValue } from "./types";
3
4
  declare const __propDef: {
4
5
  props: {
5
6
  edit: boolean;
6
- value?: string | number;
7
+ value?: CellValue;
7
8
  display_value?: string | null;
8
9
  styling?: string;
9
10
  header?: boolean;
@@ -28,7 +28,6 @@ import {
28
28
  handle_file_upload
29
29
  } from "./utils/table_utils";
30
30
  import { make_headers, process_data } from "./utils/data_processing";
31
- import { cast_value_to_type } from "./utils/utils";
32
31
  import { handle_keydown, handle_cell_blur } from "./utils/keyboard_utils";
33
32
  import {
34
33
  create_drag_handlers
@@ -180,7 +179,8 @@ $: if (!dequal(values, old_val)) {
180
179
  els,
181
180
  data_binding,
182
181
  make_id,
183
- display_value
182
+ display_value,
183
+ datatype
184
184
  );
185
185
  old_val = JSON.parse(JSON.stringify(values));
186
186
  if (is_reset || is_different_structure) {
@@ -235,26 +235,18 @@ $: if ($df_state.current_search_query !== void 0) {
235
235
  filtered_to_original_map = [];
236
236
  }
237
237
  let previous_headers = _headers.map((h) => h.value);
238
- let previous_data = data.map((row) => row.map((cell) => String(cell.value)));
238
+ let previous_data = data.map((row) => row.map((cell) => cell.value));
239
239
  $: {
240
240
  if (data || _headers) {
241
241
  df_actions.trigger_change(
242
- data.map(
243
- (row, rowIdx) => row.map((cell, colIdx) => {
244
- const dtype = Array.isArray(datatype) ? datatype[colIdx] : datatype;
245
- return {
246
- ...cell,
247
- value: cast_value_to_type(cell.value, dtype)
248
- };
249
- })
250
- ),
242
+ data,
251
243
  _headers,
252
244
  previous_data,
253
245
  previous_headers,
254
246
  value_is_output,
255
247
  dispatch
256
248
  );
257
- previous_data = data.map((row) => row.map((cell) => String(cell.value)));
249
+ previous_data = data.map((row) => row.map((cell) => cell.value));
258
250
  previous_headers = _headers.map((h) => h.value);
259
251
  }
260
252
  }
@@ -2,14 +2,15 @@ import { SvelteComponent } from "svelte";
2
2
  import type { SelectData } from "@gradio/utils";
3
3
  import type { I18nFormatter } from "js/core/src/gradio_helper";
4
4
  import { type Client } from "@gradio/client";
5
- import type { Headers, DataframeValue, Datatype } from "./utils/utils";
5
+ import type { Headers, DataframeValue, Datatype, EditData } from "./utils/utils";
6
+ import type { CellValue } from "./types";
6
7
  declare const __propDef: {
7
8
  props: {
8
9
  datatype: Datatype | Datatype[];
9
10
  label?: string | null;
10
11
  show_label?: boolean;
11
12
  headers?: Headers;
12
- values?: (string | number)[][];
13
+ values?: CellValue[][];
13
14
  col_count: [number, "fixed" | "dynamic"];
14
15
  row_count: [number, "fixed" | "dynamic"];
15
16
  latex_delimiters: {
@@ -46,6 +47,7 @@ declare const __propDef: {
46
47
  input: CustomEvent<undefined>;
47
48
  select: CustomEvent<SelectData>;
48
49
  search: CustomEvent<string | null>;
50
+ edit: CustomEvent<EditData>;
49
51
  } & {
50
52
  [evt: string]: CustomEvent<any>;
51
53
  };
@@ -1,9 +1,10 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import type { I18nFormatter } from "js/core/src/gradio_helper";
3
3
  import type { Datatype } from "./utils/utils";
4
+ import type { CellValue } from "./types";
4
5
  declare const __propDef: {
5
6
  props: {
6
- value: string | number;
7
+ value: CellValue;
7
8
  index: number;
8
9
  j: number;
9
10
  actual_pinned_columns: number;
@@ -1,4 +1,5 @@
1
1
  import { writable } from "svelte/store";
2
+ import type { CellValue } from "../types";
2
3
  import { get_next_cell_coordinates, get_range_selection, move_cursor } from "../utils/selection_utils";
3
4
  export declare const DATAFRAME_KEY: unique symbol;
4
5
  export type SortDirection = "asc" | "desc";
@@ -18,7 +19,7 @@ interface DataFrameState {
18
19
  max_height: number;
19
20
  column_widths: string[];
20
21
  max_chars?: number;
21
- static_columns?: (string | number)[];
22
+ static_columns?: CellValue[];
22
23
  };
23
24
  current_search_query: string | null;
24
25
  sort_state: {
@@ -30,7 +31,7 @@ interface DataFrameState {
30
31
  initial_data: {
31
32
  data: {
32
33
  id: string;
33
- value: string | number;
34
+ value: CellValue;
34
35
  }[][];
35
36
  display_value: string[][] | null;
36
37
  styling: string[][] | null;
@@ -46,7 +47,7 @@ interface DataFrameState {
46
47
  initial_data: {
47
48
  data: {
48
49
  id: string;
49
- value: string | number;
50
+ value: CellValue;
50
51
  }[][];
51
52
  display_value: string[][] | null;
52
53
  styling: string[][] | null;
@@ -105,7 +106,7 @@ interface DataFrameActions {
105
106
  data: any[][];
106
107
  headers: string[];
107
108
  };
108
- trigger_change: (data: any[][], headers: any[], previous_data: string[][], previous_headers: string[], value_is_output: boolean, dispatch: (e: "change" | "input", detail?: any) => void) => Promise<void>;
109
+ trigger_change: (data: any[][], headers: any[], previous_data: any[][], previous_headers: string[], value_is_output: boolean, dispatch: (e: "change" | "input" | "edit", detail?: any) => void) => Promise<void>;
109
110
  reset_sort_state: () => void;
110
111
  reset_filter_state: () => void;
111
112
  set_active_cell_menu: (menu: {
@@ -169,10 +170,10 @@ export interface DataFrameContext {
169
170
  input: HTMLTextAreaElement | null;
170
171
  }>;
171
172
  parent_element?: HTMLElement;
172
- get_data_at?: (row: number, col: number) => string | number;
173
- get_column?: (col: number) => (string | number)[];
174
- get_row?: (row: number) => (string | number)[];
175
- dispatch?: (e: "change" | "select" | "search", detail?: any) => void;
173
+ get_data_at?: (row: number, col: number) => CellValue;
174
+ get_column?: (col: number) => CellValue[];
175
+ get_row?: (row: number) => CellValue[];
176
+ dispatch?: (e: "change" | "select" | "search" | "edit", detail?: any) => void;
176
177
  }
177
178
  export declare function create_dataframe_context(config: DataFrameState["config"]): DataFrameContext;
178
179
  export declare function get_dataframe_context(): DataFrameContext;
@@ -152,7 +152,7 @@ function create_actions(state, context) {
152
152
  if (s.current_search_query)
153
153
  return;
154
154
  const current_headers = headers.map((h) => h.value);
155
- const current_data = data.map((row) => row.map((cell) => String(cell.value)));
155
+ const current_data = data.map((row) => row.map((cell) => cell.value));
156
156
  if (!dequal(current_data, previous_data) ||
157
157
  !dequal(current_headers, previous_headers)) {
158
158
  if (!dequal(current_headers, previous_headers)) {
@@ -166,6 +166,14 @@ function create_actions(state, context) {
166
166
  headers: current_headers,
167
167
  metadata: null
168
168
  });
169
+ const index = s.ui_state.selected;
170
+ if (index) {
171
+ dispatch("edit", {
172
+ index,
173
+ value: data[index[0]][index[1]].value,
174
+ previous_value: previous_data[index[0]][index[1]]
175
+ });
176
+ }
169
177
  if (!value_is_output)
170
178
  dispatch("input");
171
179
  }
@@ -5,9 +5,10 @@ export interface HeadersWithIDs {
5
5
  id: string;
6
6
  value: string;
7
7
  }
8
+ export type CellValue = string | number | boolean;
8
9
  export interface TableCell {
9
10
  id: string;
10
- value: string | number;
11
+ value: CellValue;
11
12
  }
12
13
  export type TableData = TableCell[][];
13
14
  export type CountConfig = [number, "fixed" | "dynamic"];
@@ -1,13 +1,14 @@
1
- import type { Headers, HeadersWithIDs } from "./utils";
1
+ import type { Datatype, Headers, HeadersWithIDs } from "./utils";
2
+ import type { CellValue } from "../types";
2
3
  export declare function make_headers(_head: Headers, col_count: [number, "fixed" | "dynamic"], els: Record<string, {
3
4
  cell: null | HTMLTableCellElement;
4
5
  input: null | HTMLTextAreaElement;
5
6
  }>, make_id: () => string): HeadersWithIDs;
6
- export declare function process_data(values: (string | number)[][], els: Record<string, {
7
+ export declare function process_data(values: CellValue[][], els: Record<string, {
7
8
  cell: null | HTMLTableCellElement;
8
9
  input: null | HTMLTextAreaElement;
9
- }>, data_binding: Record<string, any>, make_id: () => string, display_value?: string[][] | null): {
10
+ }>, data_binding: Record<string, any>, make_id: () => string, display_value: (string[][] | null) | undefined, datatype: Datatype | Datatype[]): {
10
11
  id: string;
11
- value: string | number;
12
+ value: CellValue;
12
13
  display_value?: string;
13
14
  }[][];
@@ -1,3 +1,4 @@
1
+ import { cast_value_to_type } from "./utils";
1
2
  export function make_headers(_head, col_count, els, make_id) {
2
3
  let _h = _head || [];
3
4
  if (col_count[1] === "fixed" && _h.length < col_count[0]) {
@@ -21,7 +22,7 @@ export function make_headers(_head, col_count, els, make_id) {
21
22
  return { id: _id, value: h ?? "" };
22
23
  });
23
24
  }
24
- export function process_data(values, els, data_binding, make_id, display_value = null) {
25
+ export function process_data(values, els, data_binding, make_id, display_value = null, datatype) {
25
26
  if (!values || values.length === 0) {
26
27
  return [];
27
28
  }
@@ -34,9 +35,10 @@ export function process_data(values, els, data_binding, make_id, display_value =
34
35
  if (display === undefined) {
35
36
  display = String(value);
36
37
  }
38
+ const dtype = Array.isArray(datatype) ? datatype[j] : datatype;
37
39
  return {
38
40
  id: _id,
39
- value,
41
+ value: cast_value_to_type(value, dtype),
40
42
  display_value: display
41
43
  };
42
44
  });
@@ -1,7 +1,8 @@
1
+ import type { CellValue } from "../types";
1
2
  export type FilterDatatype = "string" | "number";
2
3
  export declare function filter_data(data: {
3
4
  id: string;
4
- value: string | number;
5
+ value: CellValue;
5
6
  }[][], filter_columns: {
6
7
  col: number;
7
8
  datatype: FilterDatatype;
@@ -10,7 +11,7 @@ export declare function filter_data(data: {
10
11
  }[]): number[];
11
12
  export declare function filter_data_and_preserve_selection(data: {
12
13
  id: string;
13
- value: string | number;
14
+ value: CellValue;
14
15
  }[][], display_value: string[][] | null, styling: string[][] | null, filter_columns: {
15
16
  col: number;
16
17
  datatype: FilterDatatype;
@@ -18,10 +19,10 @@ export declare function filter_data_and_preserve_selection(data: {
18
19
  value: string;
19
20
  }[], selected: [number, number] | false, get_current_indices: (id: string, data: {
20
21
  id: string;
21
- value: string | number;
22
+ value: CellValue;
22
23
  }[][]) => [number, number], original_data?: {
23
24
  id: string;
24
- value: string | number;
25
+ value: CellValue;
25
26
  }[][], original_display_value?: string[][] | null, original_styling?: string[][] | null): {
26
27
  data: typeof data;
27
28
  selected: [number, number] | false;
@@ -26,7 +26,7 @@ export async function handle_cell_blur(event, ctx, coords) {
26
26
  const input_el = event.target;
27
27
  if (!input_el || input_el.value === undefined)
28
28
  return;
29
- await save_cell_value(input_el.type === "checkbox" ? String(input_el.checked) : input_el.value, ctx, coords[0], coords[1]);
29
+ await save_cell_value(input_el.value, ctx, coords[0], coords[1]);
30
30
  }
31
31
  function handle_header_navigation(event, ctx) {
32
32
  const state = get(ctx.state);
@@ -1,7 +1,7 @@
1
- import type { CellCoordinate } from "./../types";
1
+ import type { CellCoordinate, CellValue } from "./../types";
2
2
  export type CellData = {
3
3
  id: string;
4
- value: string | number;
4
+ value: CellValue;
5
5
  };
6
6
  export declare function is_cell_in_selection(coords: [number, number], selected_cells: [number, number][]): boolean;
7
7
  export declare function is_cell_selected(cell: CellCoordinate, selected_cells: CellCoordinate[]): string;
@@ -21,7 +21,7 @@ export declare function select_column(data: any[][], col: number): CellCoordinat
21
21
  export declare function select_row(data: any[][], row: number): CellCoordinate[];
22
22
  export declare function calculate_selection_positions(selected: CellCoordinate, data: {
23
23
  id: string;
24
- value: string | number;
24
+ value: CellValue;
25
25
  }[][], els: Record<string, {
26
26
  cell: HTMLTableCellElement | null;
27
27
  }>, parent: HTMLElement, table: HTMLElement): {
@@ -1,4 +1,4 @@
1
- import type { Headers } from "../types";
1
+ import type { Headers, CellValue } from "../types";
2
2
  export type SortDirection = "asc" | "desc";
3
3
  export declare function get_sort_status(name: string, sort_columns: {
4
4
  col: number;
@@ -6,20 +6,20 @@ export declare function get_sort_status(name: string, sort_columns: {
6
6
  }[], headers: Headers): "none" | "asc" | "desc";
7
7
  export declare function sort_data(data: {
8
8
  id: string;
9
- value: string | number;
9
+ value: CellValue;
10
10
  }[][], sort_columns: {
11
11
  col: number;
12
12
  direction: SortDirection;
13
13
  }[]): number[];
14
14
  export declare function sort_data_and_preserve_selection(data: {
15
15
  id: string;
16
- value: string | number;
16
+ value: CellValue;
17
17
  }[][], display_value: string[][] | null, styling: string[][] | null, sort_columns: {
18
18
  col: number;
19
19
  direction: SortDirection;
20
20
  }[], selected: [number, number] | false, get_current_indices: (id: string, data: {
21
21
  id: string;
22
- value: string | number;
22
+ value: CellValue;
23
23
  }[][]) => [number, number]): {
24
24
  data: typeof data;
25
25
  selected: [number, number] | false;
@@ -1,4 +1,4 @@
1
- import type { Headers, HeadersWithIDs, TableCell, TableData } from "../types";
1
+ import type { CellValue, Headers, HeadersWithIDs, TableCell, TableData } from "../types";
2
2
  import type { SortDirection } from "./sort_utils";
3
3
  import type { FilterDatatype } from "./filter_utils";
4
4
  export declare function make_cell_id(row: number, col: number): string;
@@ -17,4 +17,4 @@ export declare function filter_table_data(data: TableData, display_value: string
17
17
  export declare function copy_table_data(data: TableData, selected_cells: [number, number][] | null): Promise<void>;
18
18
  export declare function guess_delimiter(text: string, possibleDelimiters: string[]): string[];
19
19
  export declare function data_uri_to_blob(data_uri: string): Blob;
20
- export declare function handle_file_upload(data_uri: string, update_headers: (headers: Headers) => HeadersWithIDs[], update_values: (values: (string | number)[][]) => void): void;
20
+ export declare function handle_file_upload(data_uri: string, update_headers: (headers: Headers) => HeadersWithIDs[], update_values: (values: CellValue[][]) => void): void;
@@ -1,5 +1,6 @@
1
+ import type { CellValue } from "../types";
1
2
  export type Headers = string[];
2
- export type Data = (string | number)[][];
3
+ export type Data = CellValue[][];
3
4
  export type Datatype = "str" | "number" | "bool" | "date" | "markdown" | "html" | "image";
4
5
  export type Metadata = {
5
6
  [key: string]: string[][] | null;
@@ -19,4 +20,9 @@ export type DataframeValue = {
19
20
  * @param t - The type to coerce to.
20
21
  * @returns The coerced value.
21
22
  */
22
- export declare function cast_value_to_type(v: any, t: Datatype): string | number | boolean;
23
+ export declare function cast_value_to_type(v: any, t: Datatype): CellValue;
24
+ export interface EditData {
25
+ index: number | [number, number];
26
+ value: string;
27
+ previous_value: string;
28
+ }
@@ -15,7 +15,7 @@ export let value = {
15
15
  metadata: null
16
16
  };
17
17
  export let datatype = "str";
18
- export let editable = true;
18
+ export let interactive = true;
19
19
  export let show_row_numbers = false;
20
20
  export let max_height = 500;
21
21
  export let show_search = "none";
@@ -68,7 +68,7 @@ onMount(() => {
68
68
  display_value={value?.metadata?.display_value}
69
69
  styling={value?.metadata?.styling}
70
70
  {datatype}
71
- {editable}
71
+ editable={interactive}
72
72
  {show_row_numbers}
73
73
  {max_height}
74
74
  {show_search}
@@ -6,7 +6,7 @@ declare const __propDef: {
6
6
  i18n?: I18nFormatter | undefined;
7
7
  value?: DataframeValue;
8
8
  datatype?: Datatype | Datatype[];
9
- editable?: boolean;
9
+ interactive?: boolean;
10
10
  show_row_numbers?: boolean;
11
11
  max_height?: number;
12
12
  show_search?: "none" | "search" | "filter";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/dataframe",
3
- "version": "0.19.3",
3
+ "version": "0.20.0",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -16,12 +16,12 @@
16
16
  "@gradio/atoms": "^0.18.0",
17
17
  "@gradio/button": "^0.5.13",
18
18
  "@gradio/icons": "^0.14.0",
19
- "@gradio/statustracker": "^0.11.1",
19
+ "@gradio/markdown-code": "^0.5.2",
20
20
  "@gradio/checkbox": "^0.4.30",
21
- "@gradio/markdown-code": "^0.5.1",
21
+ "@gradio/statustracker": "^0.11.1",
22
22
  "@gradio/upload": "^0.17.0",
23
- "@gradio/client": "^1.19.0",
24
- "@gradio/utils": "^0.10.2"
23
+ "@gradio/utils": "^0.10.2",
24
+ "@gradio/client": "^1.19.0"
25
25
  },
26
26
  "exports": {
27
27
  ".": {
@@ -1,13 +1,10 @@
1
1
  <script lang="ts">
2
2
  import { BaseCheckbox } from "@gradio/checkbox";
3
3
 
4
- export let value: boolean | string = false;
4
+ export let value = false;
5
5
  export let editable = true;
6
6
  export let on_change: (value: boolean) => void;
7
7
 
8
- $: bool_value =
9
- typeof value === "string" ? value.toLowerCase() === "true" : !!value;
10
-
11
8
  function handle_change(event: CustomEvent<boolean>): void {
12
9
  if (editable) {
13
10
  on_change(event.detail);
@@ -17,7 +14,7 @@
17
14
 
18
15
  <div class="bool-cell" role="button" tabindex="-1">
19
16
  <BaseCheckbox
20
- bind:value={bool_value}
17
+ bind:value
21
18
  label=""
22
19
  interactive={editable}
23
20
  on:change={handle_change}
@@ -2,11 +2,12 @@
2
2
  import { createEventDispatcher } from "svelte";
3
3
  import { MarkdownCode } from "@gradio/markdown-code";
4
4
  import type { I18nFormatter } from "@gradio/utils";
5
+ import type { CellValue } from "./types";
5
6
  import SelectionButtons from "./icons/SelectionButtons.svelte";
6
7
  import BooleanCell from "./BooleanCell.svelte";
7
8
 
8
9
  export let edit: boolean;
9
- export let value: string | number = "";
10
+ export let value: CellValue = "";
10
11
  export let display_value: string | null = null;
11
12
  export let styling = "";
12
13
  export let header = false;
@@ -44,7 +45,7 @@
44
45
  }>();
45
46
 
46
47
  function truncate_text(
47
- text: string | number,
48
+ text: CellValue,
48
49
  max_length: number | null = null,
49
50
  is_image = false
50
51
  ): string {
@@ -86,18 +87,13 @@
86
87
  dispatch("keydown", event);
87
88
  }
88
89
 
89
- function handle_bool_change(new_value: boolean): void {
90
- value = new_value.toString();
91
- dispatch("blur", {
92
- blur_event: {
93
- target: {
94
- type: "checkbox",
95
- checked: new_value,
96
- value: new_value.toString()
97
- }
98
- } as unknown as FocusEvent,
99
- coords: coords
100
- });
90
+ function commit_change(checked: boolean): void {
91
+ handle_blur({ target: { value } } as unknown as FocusEvent);
92
+ }
93
+
94
+ $: if (!edit) {
95
+ // Shim blur on removal for Safari and Firefox
96
+ handle_blur({ target: { value } } as unknown as FocusEvent);
101
97
  }
102
98
  </script>
103
99
 
@@ -118,12 +114,8 @@
118
114
  />
119
115
  {/if}
120
116
 
121
- {#if datatype === "bool"}
122
- <BooleanCell
123
- value={String(display_content)}
124
- {editable}
125
- on_change={handle_bool_change}
126
- />
117
+ {#if datatype === "bool" && typeof value === "boolean"}
118
+ <BooleanCell bind:value {editable} on_change={commit_change} />
127
119
  {:else}
128
120
  <span
129
121
  class:dragging={is_dragging}
@@ -20,10 +20,15 @@
20
20
  import type { I18nFormatter } from "js/core/src/gradio_helper";
21
21
  import { type Client } from "@gradio/client";
22
22
  import VirtualTable from "./VirtualTable.svelte";
23
- import type { Headers, DataframeValue, Datatype } from "./utils/utils";
23
+ import type {
24
+ Headers,
25
+ DataframeValue,
26
+ Datatype,
27
+ EditData
28
+ } from "./utils/utils";
24
29
  import CellMenu from "./CellMenu.svelte";
25
30
  import Toolbar from "./Toolbar.svelte";
26
- import type { CellCoordinate } from "./types";
31
+ import type { CellCoordinate, CellValue } from "./types";
27
32
  import {
28
33
  is_cell_selected,
29
34
  should_show_cell_menu,
@@ -37,7 +42,6 @@
37
42
  handle_file_upload
38
43
  } from "./utils/table_utils";
39
44
  import { make_headers, process_data } from "./utils/data_processing";
40
- import { cast_value_to_type } from "./utils/utils";
41
45
  import { handle_keydown, handle_cell_blur } from "./utils/keyboard_utils";
42
46
  import {
43
47
  create_drag_handlers,
@@ -51,7 +55,7 @@
51
55
  export let label: string | null = null;
52
56
  export let show_label = true;
53
57
  export let headers: Headers = [];
54
- export let values: (string | number)[][] = [];
58
+ export let values: CellValue[][] = [];
55
59
  export let col_count: [number, "fixed" | "dynamic"];
56
60
  export let row_count: [number, "fixed" | "dynamic"];
57
61
  export let latex_delimiters: {
@@ -163,6 +167,7 @@
163
167
  input: undefined;
164
168
  select: SelectData;
165
169
  search: string | null;
170
+ edit: EditData;
166
171
  }>();
167
172
 
168
173
  let els: Record<
@@ -172,12 +177,11 @@
172
177
  let data_binding: Record<string, (typeof data)[0][0]> = {};
173
178
  let _headers = make_headers(headers, col_count, els, make_id);
174
179
  let old_headers: string[] = headers;
175
- let data: { id: string; value: string | number; display_value?: string }[][] =
176
- [[]];
177
- let old_val: undefined | (string | number)[][] = undefined;
180
+ let data: { id: string; value: CellValue; display_value?: string }[][] = [[]];
181
+ let old_val: undefined | CellValue[][] = undefined;
178
182
  let search_results: {
179
183
  id: string;
180
- value: string | number;
184
+ value: CellValue;
181
185
  display_value?: string;
182
186
  styling?: string;
183
187
  }[][] = [[]];
@@ -196,13 +200,13 @@
196
200
  );
197
201
  });
198
202
 
199
- const get_data_at = (row: number, col: number): string | number =>
203
+ const get_data_at = (row: number, col: number): CellValue =>
200
204
  data?.[row]?.[col]?.value;
201
205
 
202
- const get_column = (col: number): (string | number)[] =>
206
+ const get_column = (col: number): CellValue[] =>
203
207
  data?.map((row) => row[col]?.value) ?? [];
204
208
 
205
- const get_row = (row: number): (string | number)[] =>
209
+ const get_row = (row: number): CellValue[] =>
206
210
  data?.[row]?.map((cell) => cell.value) ?? [];
207
211
 
208
212
  $: {
@@ -248,13 +252,14 @@
248
252
  (values[0] && old_val[0] && values[0].length !== old_val[0].length));
249
253
 
250
254
  data = process_data(
251
- values as (string | number)[][],
255
+ values as CellValue[][],
252
256
  els,
253
257
  data_binding,
254
258
  make_id,
255
- display_value
259
+ display_value,
260
+ datatype
256
261
  );
257
- old_val = JSON.parse(JSON.stringify(values)) as (string | number)[][];
262
+ old_val = JSON.parse(JSON.stringify(values)) as CellValue[][];
258
263
 
259
264
  if (is_reset || is_different_structure) {
260
265
  df_actions.reset_sort_state();
@@ -326,27 +331,19 @@
326
331
  }
327
332
 
328
333
  let previous_headers = _headers.map((h) => h.value);
329
- let previous_data = data.map((row) => row.map((cell) => String(cell.value)));
334
+ let previous_data = data.map((row) => row.map((cell) => cell.value));
330
335
 
331
336
  $: {
332
337
  if (data || _headers) {
333
338
  df_actions.trigger_change(
334
- data.map((row, rowIdx) =>
335
- row.map((cell, colIdx) => {
336
- const dtype = Array.isArray(datatype) ? datatype[colIdx] : datatype;
337
- return {
338
- ...cell,
339
- value: cast_value_to_type(cell.value, dtype)
340
- };
341
- })
342
- ),
339
+ data,
343
340
  _headers,
344
341
  previous_data,
345
342
  previous_headers,
346
343
  value_is_output,
347
344
  dispatch
348
345
  );
349
- previous_data = data.map((row) => row.map((cell) => String(cell.value)));
346
+ previous_data = data.map((row) => row.map((cell) => cell.value));
350
347
  previous_headers = _headers.map((h) => h.value);
351
348
  }
352
349
  }
@@ -675,12 +672,12 @@
675
672
 
676
673
  function commit_filter(): void {
677
674
  if ($df_state.current_search_query && show_search === "filter") {
678
- const filtered_data: (string | number)[][] = [];
675
+ const filtered_data: CellValue[][] = [];
679
676
  const filtered_display_values: string[][] = [];
680
677
  const filtered_styling: string[][] = [];
681
678
 
682
679
  search_results.forEach((row) => {
683
- const data_row: (string | number)[] = [];
680
+ const data_row: CellValue[] = [];
684
681
  const display_row: string[] = [];
685
682
  const styling_row: string[] = [];
686
683
 
@@ -3,9 +3,10 @@
3
3
  import CellMenuButton from "./CellMenuButton.svelte";
4
4
  import type { I18nFormatter } from "js/core/src/gradio_helper";
5
5
  import type { Datatype } from "./utils/utils";
6
+ import type { CellValue } from "./types";
6
7
  import { is_cell_in_selection } from "./utils/selection_utils";
7
8
 
8
- export let value: string | number;
9
+ export let value: CellValue;
9
10
  export let index: number;
10
11
  export let j: number;
11
12
  export let actual_pinned_columns: number;
@@ -2,6 +2,7 @@ import { getContext, setContext } from "svelte";
2
2
  import { dequal } from "dequal";
3
3
  import { writable, get } from "svelte/store";
4
4
  import { sort_table_data } from "../utils/table_utils";
5
+ import type { CellValue } from "../types";
5
6
  import { tick } from "svelte";
6
7
  import {
7
8
  handle_selection,
@@ -30,14 +31,14 @@ interface DataFrameState {
30
31
  max_height: number;
31
32
  column_widths: string[];
32
33
  max_chars?: number;
33
- static_columns?: (string | number)[];
34
+ static_columns?: CellValue[];
34
35
  };
35
36
  current_search_query: string | null;
36
37
  sort_state: {
37
38
  sort_columns: { col: number; direction: SortDirection }[];
38
39
  row_order: number[];
39
40
  initial_data: {
40
- data: { id: string; value: string | number }[][];
41
+ data: { id: string; value: CellValue }[][];
41
42
  display_value: string[][] | null;
42
43
  styling: string[][] | null;
43
44
  } | null;
@@ -50,7 +51,7 @@ interface DataFrameState {
50
51
  value: string;
51
52
  }[];
52
53
  initial_data: {
53
- data: { id: string; value: string | number }[][];
54
+ data: { id: string; value: CellValue }[][];
54
55
  display_value: string[][] | null;
55
56
  styling: string[][] | null;
56
57
  } | null;
@@ -124,10 +125,10 @@ interface DataFrameActions {
124
125
  trigger_change: (
125
126
  data: any[][],
126
127
  headers: any[],
127
- previous_data: string[][],
128
+ previous_data: any[][],
128
129
  previous_headers: string[],
129
130
  value_is_output: boolean,
130
- dispatch: (e: "change" | "input", detail?: any) => void
131
+ dispatch: (e: "change" | "input" | "edit", detail?: any) => void
131
132
  ) => Promise<void>;
132
133
  reset_sort_state: () => void;
133
134
  reset_filter_state: () => void;
@@ -183,10 +184,10 @@ export interface DataFrameContext {
183
184
  { cell: HTMLTableCellElement | null; input: HTMLTextAreaElement | null }
184
185
  >;
185
186
  parent_element?: HTMLElement;
186
- get_data_at?: (row: number, col: number) => string | number;
187
- get_column?: (col: number) => (string | number)[];
188
- get_row?: (row: number) => (string | number)[];
189
- dispatch?: (e: "change" | "select" | "search", detail?: any) => void;
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;
190
191
  }
191
192
 
192
193
  function create_actions(
@@ -230,7 +231,7 @@ function create_actions(
230
231
  };
231
232
 
232
233
  const update_array = (
233
- source: { id: string; value: string | number }[][] | string[][] | null,
234
+ source: { id: string; value: CellValue }[][] | string[][] | null,
234
235
  target: any[] | null | undefined
235
236
  ): void => {
236
237
  if (source && target) {
@@ -397,9 +398,7 @@ function create_actions(
397
398
  if (s.current_search_query) return;
398
399
 
399
400
  const current_headers = headers.map((h) => h.value);
400
- const current_data = data.map((row) =>
401
- row.map((cell) => String(cell.value))
402
- );
401
+ const current_data = data.map((row) => row.map((cell) => cell.value));
403
402
 
404
403
  if (
405
404
  !dequal(current_data, previous_data) ||
@@ -416,6 +415,14 @@ function create_actions(
416
415
  headers: current_headers,
417
416
  metadata: null
418
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
+ }
419
426
  if (!value_is_output) dispatch("input");
420
427
  }
421
428
  },
package/shared/types.ts CHANGED
@@ -9,9 +9,11 @@ export interface HeadersWithIDs {
9
9
  }
10
10
  [];
11
11
 
12
+ export type CellValue = string | number | boolean;
13
+
12
14
  export interface TableCell {
13
15
  id: string;
14
- value: string | number;
16
+ value: CellValue;
15
17
  }
16
18
 
17
19
  export type TableData = TableCell[][];
@@ -1,4 +1,6 @@
1
- import type { Headers, HeadersWithIDs } from "./utils";
1
+ import type { Datatype, Headers, HeadersWithIDs } from "./utils";
2
+ import type { CellValue } from "../types";
3
+ import { cast_value_to_type } from "./utils";
2
4
 
3
5
  export function make_headers(
4
6
  _head: Headers,
@@ -35,15 +37,16 @@ export function make_headers(
35
37
  }
36
38
 
37
39
  export function process_data(
38
- values: (string | number)[][],
40
+ values: CellValue[][],
39
41
  els: Record<
40
42
  string,
41
43
  { cell: null | HTMLTableCellElement; input: null | HTMLTextAreaElement }
42
44
  >,
43
45
  data_binding: Record<string, any>,
44
46
  make_id: () => string,
45
- display_value: string[][] | null = null
46
- ): { id: string; value: string | number; display_value?: string }[][] {
47
+ display_value: string[][] | null = null,
48
+ datatype: Datatype | Datatype[]
49
+ ): { id: string; value: CellValue; display_value?: string }[][] {
47
50
  if (!values || values.length === 0) {
48
51
  return [];
49
52
  }
@@ -60,9 +63,11 @@ export function process_data(
60
63
  display = String(value);
61
64
  }
62
65
 
66
+ const dtype = Array.isArray(datatype) ? datatype[j] : datatype;
67
+
63
68
  return {
64
69
  id: _id,
65
- value,
70
+ value: cast_value_to_type(value, dtype),
66
71
  display_value: display
67
72
  };
68
73
  });
@@ -1,9 +1,10 @@
1
1
  import { filter_table_data } from "./table_utils";
2
+ import type { CellValue } from "../types";
2
3
 
3
4
  export type FilterDatatype = "string" | "number";
4
5
 
5
6
  export function filter_data(
6
- data: { id: string; value: string | number }[][],
7
+ data: { id: string; value: CellValue }[][],
7
8
  filter_columns: {
8
9
  col: number;
9
10
  datatype: FilterDatatype;
@@ -164,7 +165,7 @@ export function filter_data(
164
165
  }
165
166
 
166
167
  export function filter_data_and_preserve_selection(
167
- data: { id: string; value: string | number }[][],
168
+ data: { id: string; value: CellValue }[][],
168
169
  display_value: string[][] | null,
169
170
  styling: string[][] | null,
170
171
  filter_columns: {
@@ -176,9 +177,9 @@ export function filter_data_and_preserve_selection(
176
177
  selected: [number, number] | false,
177
178
  get_current_indices: (
178
179
  id: string,
179
- data: { id: string; value: string | number }[][]
180
+ data: { id: string; value: CellValue }[][]
180
181
  ) => [number, number],
181
- original_data?: { id: string; value: string | number }[][],
182
+ original_data?: { id: string; value: CellValue }[][],
182
183
  original_display_value?: string[][] | null,
183
184
  original_styling?: string[][] | null
184
185
  ): { data: typeof data; selected: [number, number] | false } {
@@ -40,12 +40,7 @@ export async function handle_cell_blur(
40
40
  const input_el = event.target as HTMLInputElement;
41
41
  if (!input_el || input_el.value === undefined) return;
42
42
 
43
- await save_cell_value(
44
- input_el.type === "checkbox" ? String(input_el.checked) : input_el.value,
45
- ctx,
46
- coords[0],
47
- coords[1]
48
- );
43
+ await save_cell_value(input_el.value, ctx, coords[0], coords[1]);
49
44
  }
50
45
 
51
46
  function handle_header_navigation(
@@ -1,6 +1,6 @@
1
- import type { CellCoordinate } from "./../types";
1
+ import type { CellCoordinate, CellValue } from "./../types";
2
2
 
3
- export type CellData = { id: string; value: string | number };
3
+ export type CellData = { id: string; value: CellValue };
4
4
 
5
5
  export function is_cell_in_selection(
6
6
  coords: [number, number],
@@ -208,7 +208,7 @@ export function select_row(data: any[][], row: number): CellCoordinate[] {
208
208
 
209
209
  export function calculate_selection_positions(
210
210
  selected: CellCoordinate,
211
- data: { id: string; value: string | number }[][],
211
+ data: { id: string; value: CellValue }[][],
212
212
  els: Record<string, { cell: HTMLTableCellElement | null }>,
213
213
  parent: HTMLElement,
214
214
  table: HTMLElement
@@ -1,4 +1,4 @@
1
- import type { Headers } from "../types";
1
+ import type { Headers, CellValue } from "../types";
2
2
  import { sort_table_data } from "./table_utils";
3
3
 
4
4
  export type SortDirection = "asc" | "desc";
@@ -21,7 +21,7 @@ export function get_sort_status(
21
21
  }
22
22
 
23
23
  export function sort_data(
24
- data: { id: string; value: string | number }[][],
24
+ data: { id: string; value: CellValue }[][],
25
25
  sort_columns: { col: number; direction: SortDirection }[]
26
26
  ): number[] {
27
27
  if (!data || !data.length || !data[0]) {
@@ -64,14 +64,14 @@ export function sort_data(
64
64
  }
65
65
 
66
66
  export function sort_data_and_preserve_selection(
67
- data: { id: string; value: string | number }[][],
67
+ data: { id: string; value: CellValue }[][],
68
68
  display_value: string[][] | null,
69
69
  styling: string[][] | null,
70
70
  sort_columns: { col: number; direction: SortDirection }[],
71
71
  selected: [number, number] | false,
72
72
  get_current_indices: (
73
73
  id: string,
74
- data: { id: string; value: string | number }[][]
74
+ data: { id: string; value: CellValue }[][]
75
75
  ) => [number, number]
76
76
  ): { data: typeof data; selected: [number, number] | false } {
77
77
  let id = null;
@@ -1,4 +1,10 @@
1
- import type { Headers, HeadersWithIDs, TableCell, TableData } from "../types";
1
+ import type {
2
+ CellValue,
3
+ Headers,
4
+ HeadersWithIDs,
5
+ TableCell,
6
+ TableData
7
+ } from "../types";
2
8
  import { sort_data } from "./sort_utils";
3
9
  import { filter_data } from "./filter_utils";
4
10
  import type { SortDirection } from "./sort_utils";
@@ -173,7 +179,7 @@ export function data_uri_to_blob(data_uri: string): Blob {
173
179
  export function handle_file_upload(
174
180
  data_uri: string,
175
181
  update_headers: (headers: Headers) => HeadersWithIDs[],
176
- update_values: (values: (string | number)[][]) => void
182
+ update_values: (values: CellValue[][]) => void
177
183
  ): void {
178
184
  const blob = data_uri_to_blob(data_uri);
179
185
  const reader = new FileReader();
@@ -1,5 +1,7 @@
1
+ import type { CellValue } from "../types";
2
+
1
3
  export type Headers = string[];
2
- export type Data = (string | number)[][];
4
+ export type Data = CellValue[][];
3
5
 
4
6
  export type Datatype =
5
7
  | "str"
@@ -26,10 +28,7 @@ export type DataframeValue = {
26
28
  * @param t - The type to coerce to.
27
29
  * @returns The coerced value.
28
30
  */
29
- export function cast_value_to_type(
30
- v: any,
31
- t: Datatype
32
- ): string | number | boolean {
31
+ export function cast_value_to_type(v: any, t: Datatype): CellValue {
33
32
  if (t === "number") {
34
33
  const n = Number(v);
35
34
  return isNaN(n) ? v : n;
@@ -48,3 +47,9 @@ export function cast_value_to_type(
48
47
  }
49
48
  return v;
50
49
  }
50
+
51
+ export interface EditData {
52
+ index: number | [number, number];
53
+ value: string;
54
+ previous_value: string;
55
+ }
@@ -20,7 +20,7 @@
20
20
  metadata: null
21
21
  };
22
22
  export let datatype: Datatype | Datatype[] = "str";
23
- export let editable = true;
23
+ export let interactive = true;
24
24
  export let show_row_numbers = false;
25
25
  export let max_height = 500;
26
26
  export let show_search: "none" | "search" | "filter" = "none";
@@ -96,7 +96,7 @@
96
96
  display_value={value?.metadata?.display_value}
97
97
  styling={value?.metadata?.styling}
98
98
  {datatype}
99
- {editable}
99
+ editable={interactive}
100
100
  {show_row_numbers}
101
101
  {max_height}
102
102
  {show_search}