@gradio/dataframe 0.24.4 → 0.24.6

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.
@@ -8,11 +8,12 @@ export function make_header_id(col) {
8
8
  export async function copy_table_data(data, selected_cells) {
9
9
  if (!data || !data.length)
10
10
  return;
11
- const cells_to_copy = selected_cells ||
12
- data.flatMap((row, r) => row.map((_, c) => [r, c]));
13
- const csv = cells_to_copy.reduce((acc, [row, col]) => {
11
+ const csv = selected_cells.reduce((acc, [row, col]) => {
14
12
  acc[row] = acc[row] || {};
15
- const value = String(data[row][col].value);
13
+ // only the headers are padded out to the column count on the way in, so
14
+ // a selection can name a cell a short row does not have
15
+ const cell = data[row]?.[col];
16
+ const value = cell ? String(cell.value) : "";
16
17
  acc[row][col] =
17
18
  value.includes(",") || value.includes('"') || value.includes("\n")
18
19
  ? `"${value.replace(/"/g, '""')}"`
@@ -22,7 +23,9 @@ export async function copy_table_data(data, selected_cells) {
22
23
  const rows = Object.keys(csv).sort((a, b) => +a - +b);
23
24
  if (!rows.length)
24
25
  return;
25
- const cols = Object.keys(csv[rows[0]]).sort((a, b) => +a - +b);
26
+ // every column any selected row contributes: taking them from the first row
27
+ // alone drops the rest of a selection that is not rectangular there
28
+ const cols = Array.from(new Set(rows.flatMap((r) => Object.keys(csv[r])))).sort((a, b) => +a - +b);
26
29
  const text = rows
27
30
  .map((r) => cols.map((c) => csv[r][c] || "").join(","))
28
31
  .join("\n");
@@ -33,41 +36,43 @@ export async function copy_table_data(data, selected_cells) {
33
36
  throw new Error("Failed to copy to clipboard: " + err.message);
34
37
  }
35
38
  }
36
- export function guess_delimiter(text, possibleDelimiters) {
37
- return possibleDelimiters.filter(weedOut);
38
- function weedOut(delimiter) {
39
- var cache = -1;
40
- return text.split("\n").every(checkLength);
41
- function checkLength(line) {
42
- if (!line)
43
- return true;
44
- var length = line.split(delimiter).length;
45
- if (cache < 0)
46
- cache = length;
47
- return cache === length && length > 1;
39
+ export async function parse_table_file(file) {
40
+ const text = await file.text();
41
+ if (!text.trim())
42
+ return { headers: [], values: [] };
43
+ // the drop zone only accepts .csv and .tsv, so the extension goes first and
44
+ // wins any tie, but a mislabeled file is still read by its content: a
45
+ // separator only takes the file if it splits every line the same way, which
46
+ // is what tells a real separator from one that sits in a header name. the
47
+ // split is d3's rather than a raw count, so a separator inside a quoted
48
+ // field does not throw the detection off.
49
+ const by_extension = file.name.toLowerCase().endsWith(".tsv") ? "\t" : ",";
50
+ const candidates = by_extension === "\t" ? ["\t", ","] : [",", "\t"];
51
+ let consistent;
52
+ let ragged;
53
+ let unsplit;
54
+ for (const delimiter of candidates) {
55
+ // a blank line parses to a single empty field, which both the width
56
+ // checks below and the caller's ragged check would read as a short row
57
+ const parsed = dsvFormat(delimiter)
58
+ .parseRows(text)
59
+ .filter((row) => row.length > 1 || row[0] !== "");
60
+ const width = parsed[0]?.length ?? 0;
61
+ // the extension is first in `candidates`, so this keeps its parse for the
62
+ // single-column case rather than running it again at the end
63
+ unsplit ??= parsed;
64
+ if (width < 2)
65
+ continue;
66
+ if (parsed.every((row) => row.length === width)) {
67
+ consistent = parsed;
68
+ break;
48
69
  }
70
+ // a separator that splits the header but leaves the rows uneven is
71
+ // usually the one that just happens to appear in a header name. keep it
72
+ // only in case the other does not split at all, where a ragged table is
73
+ // still better to report on than an unsplit one
74
+ ragged ??= parsed;
49
75
  }
50
- }
51
- export function data_uri_to_blob(data_uri) {
52
- const byte_str = atob(data_uri.split(",")[1]);
53
- const mime_str = data_uri.split(",")[0].split(":")[1].split(";")[0];
54
- const ab = new ArrayBuffer(byte_str.length);
55
- const ia = new Uint8Array(ab);
56
- for (let i = 0; i < byte_str.length; i++) {
57
- ia[i] = byte_str.charCodeAt(i);
58
- }
59
- return new Blob([ab], { type: mime_str });
60
- }
61
- export function handle_file_upload(data_uri, update_headers, update_values) {
62
- const blob = data_uri_to_blob(data_uri);
63
- const reader = new FileReader();
64
- reader.addEventListener("loadend", (e) => {
65
- if (!e?.target?.result || typeof e.target.result !== "string")
66
- return;
67
- const [delimiter] = guess_delimiter(e.target.result, [",", "\t"]);
68
- const [head, ...rest] = dsvFormat(delimiter).parseRows(e.target.result);
69
- update_headers(head);
70
- update_values(rest);
71
- });
72
- reader.readAsText(blob);
76
+ const [head = [], ...rest] = consistent ?? ragged ?? unsplit ?? [];
77
+ return { headers: head.map((h) => h ?? ""), values: rest };
73
78
  }
@@ -5,10 +5,6 @@ export type Datatype = "str" | "number" | "bool" | "date" | "markdown" | "html"
5
5
  export type Metadata = {
6
6
  [key: string]: string[][] | null;
7
7
  } | null;
8
- export type HeadersWithIDs = {
9
- value: string;
10
- id: string;
11
- }[];
12
8
  export type DataframeValue = {
13
9
  data: Data;
14
10
  headers: Headers;
@@ -5,6 +5,13 @@ export const default_i18n = {
5
5
  "dataframe.add_column_left": "Add column left",
6
6
  "dataframe.add_column_right": "Add column right",
7
7
  "dataframe.delete_column": "Delete column",
8
+ "dataframe.drop_to_upload": "Drop CSV or TSV files here to import data into dataframe",
9
+ "dataframe.import_empty_file": "The dropped file is empty.",
10
+ "dataframe.import_no_column_names": "The dropped file has no column names.",
11
+ "dataframe.import_ragged_row": "Line {line} of the file has {fields} fields, the header has {columns}.",
12
+ "dataframe.import_static_columns": "Cannot import into a table with read-only columns.",
13
+ "dataframe.import_fixed_columns": "This table takes exactly {expected} columns, the file has {actual}.",
14
+ "dataframe.import_fixed_rows": "This table takes exactly {expected} rows, the file has {actual}.",
8
15
  "dataframe.sort_asc": "Sort ascending",
9
16
  "dataframe.sort_desc": "Sort descending",
10
17
  "dataframe.sort_ascending": "Sort ascending",
package/dist/types.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface DataframeEvents {
6
6
  input: never;
7
7
  select: SelectData;
8
8
  edit: EditData;
9
+ error: string;
9
10
  clear_status: LoadingStatus;
10
11
  }
11
12
  export interface DataframeProps {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/dataframe",
3
- "version": "0.24.4",
3
+ "version": "0.24.6",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -16,14 +16,14 @@
16
16
  "@tanstack/table-core": "^8.21.3",
17
17
  "@tanstack/virtual-core": "^3.13.6",
18
18
  "@gradio/atoms": "^0.26.2",
19
+ "@gradio/button": "^0.8.3",
19
20
  "@gradio/checkbox": "^0.8.3",
20
- "@gradio/button": "^0.8.2",
21
+ "@gradio/client": "^2.7.1",
21
22
  "@gradio/icons": "^0.16.0",
22
- "@gradio/client": "^2.6.0",
23
23
  "@gradio/markdown-code": "^0.10.1",
24
- "@gradio/upload": "^0.18.3",
25
- "@gradio/statustracker": "^0.15.3",
26
- "@gradio/utils": "^0.14.1"
24
+ "@gradio/statustracker": "^0.15.4",
25
+ "@gradio/upload": "^0.19.0",
26
+ "@gradio/utils": "^0.14.2"
27
27
  },
28
28
  "exports": {
29
29
  ".": {
@@ -42,7 +42,7 @@
42
42
  "svelte": "^5.48.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@gradio/preview": "^0.17.0"
45
+ "@gradio/preview": "^0.17.1"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",
@@ -16,7 +16,6 @@
16
16
  color: var(--body-text-color);
17
17
  cursor: pointer;
18
18
  transition: all 150ms;
19
- margin-top: var(--size-2);
20
19
  z-index: 10;
21
20
  position: relative;
22
21
  pointer-events: auto;