diff --git a/.changeset/large-buttons-look.md b/.changeset/large-buttons-look.md new file mode 100644 index 0000000000000..44a86e8734e15 --- /dev/null +++ b/.changeset/large-buttons-look.md @@ -0,0 +1,7 @@ +--- +"@gradio/core": minor +"@gradio/dataframe": minor +"gradio": minor +--- + +feat:Update gr.Dataframe UI with action popover diff --git a/js/core/src/lang/en.json b/js/core/src/lang/en.json index ac71d08578ac3..33512377d3275 100644 --- a/js/core/src/lang/en.json +++ b/js/core/src/lang/en.json @@ -55,8 +55,12 @@ }, "dataframe": { "incorrect_format": "Incorrect format, only CSV and TSV files are supported", - "new_column": "New column", - "new_row": "New row" + "new_column": "Add column", + "new_row": "New row", + "add_row_above": "Add row above", + "add_row_below": "Add row below", + "add_column_left": "Add column to the left", + "add_column_right": "Add column to the right" }, "dropdown": { "dropdown": "Dropdown" diff --git a/js/dataframe/Dataframe.stories.svelte b/js/dataframe/Dataframe.stories.svelte index 818a3094dcce5..2746617c8ff6b 100644 --- a/js/dataframe/Dataframe.stories.svelte +++ b/js/dataframe/Dataframe.stories.svelte @@ -1,6 +1,10 @@ + + { + const canvas = within(canvasElement); + + const cell = canvas.getByText("200"); + userEvent.click(cell); + const open_dialog_btn = canvas.getAllByText("⋮"); + await userEvent.click(open_dialog_btn[0]); + + const add_row_btn = canvas.getByText("Add row above"); + await userEvent.click(add_row_btn); + }} +/> diff --git a/js/dataframe/shared/Arrow.svelte b/js/dataframe/shared/Arrow.svelte new file mode 100644 index 0000000000000..52000514b5a61 --- /dev/null +++ b/js/dataframe/shared/Arrow.svelte @@ -0,0 +1,10 @@ + + + + + diff --git a/js/dataframe/shared/CellMenu.svelte b/js/dataframe/shared/CellMenu.svelte new file mode 100644 index 0000000000000..7d8bee95f3869 --- /dev/null +++ b/js/dataframe/shared/CellMenu.svelte @@ -0,0 +1,108 @@ + + +
+ + + + +
+ + diff --git a/js/dataframe/shared/Table.svelte b/js/dataframe/shared/Table.svelte index c6b666cabe1dd..8361c4dff9027 100644 --- a/js/dataframe/shared/Table.svelte +++ b/js/dataframe/shared/Table.svelte @@ -4,19 +4,14 @@ import { dequal } from "dequal/lite"; import { copy } from "@gradio/utils"; import { Upload } from "@gradio/upload"; - import { BaseButton } from "@gradio/button"; + import EditableCell from "./EditableCell.svelte"; import type { SelectData } from "@gradio/utils"; import type { I18nFormatter } from "js/core/src/gradio_helper"; import { type Client } from "@gradio/client"; import VirtualTable from "./VirtualTable.svelte"; - import type { - Headers, - HeadersWithIDs, - Data, - Metadata, - Datatype - } from "./utils"; + import type { Headers, HeadersWithIDs, Metadata, Datatype } from "./utils"; + import CellMenu from "./CellMenu.svelte"; export let datatype: Datatype | Datatype[]; export let label: string | null = null; @@ -64,7 +59,7 @@ $: { if (selected !== false) { const [row, col] = selected; - if (!isNaN(row) && !isNaN(col)) { + if (!isNaN(row) && !isNaN(col) && data[row]) { dispatch("select", { index: [row, col], value: get_data_at(row, col), @@ -346,7 +341,14 @@ } } + let active_cell: { row: number; col: number } | null = null; + async function handle_cell_click(i: number, j: number): Promise { + if (active_cell && active_cell.row === i && active_cell.col === j) { + active_cell = null; + } else { + active_cell = { row: i, col: j }; + } if (dequal(editing, [i, j])) return; header_edit = false; selected_header = false; @@ -410,35 +412,39 @@ return; } - data.splice( - index ? index + 1 : data.length, - 0, - Array(data[0].length) - .fill(0) - .map((_, i) => { - const _id = make_id(); + const new_row = Array(data[0].length) + .fill(0) + .map((_, i) => { + const _id = make_id(); + els[_id] = { cell: null, input: null }; + return { id: _id, value: "" }; + }); - els[_id] = { cell: null, input: null }; - return { id: _id, value: "" }; - }) - ); + if (index !== undefined && index >= 0 && index <= data.length) { + data.splice(index, 0, new_row); + } else { + data.push(new_row); + } data = data; - selected = [index ? index + 1 : data.length - 1, 0]; + selected = [index !== undefined ? index : data.length - 1, 0]; } $: (data || selected_header) && trigger_change(); - async function add_col(): Promise { + async function add_col(index?: number): Promise { parent.focus(); if (col_count[1] !== "dynamic") return; + + const insert_index = index !== undefined ? index : data[0].length; + for (let i = 0; i < data.length; i++) { const _id = make_id(); els[_id] = { cell: null, input: null }; - data[i].push({ id: _id, value: "" }); + data[i].splice(insert_index, 0, { id: _id, value: "" }); } - headers.push(`Header ${headers.length + 1}`); + headers.splice(insert_index, 0, `Header ${headers.length + 1}`); data = data; headers = headers; @@ -446,13 +452,20 @@ await tick(); requestAnimationFrame(() => { - edit_header(headers.length - 1, true); + edit_header(insert_index, true); const new_w = parent.querySelectorAll("tbody")[1].offsetWidth; parent.querySelectorAll("table")[1].scrollTo({ left: new_w }); }); } function handle_click_outside(event: Event): void { + if ( + active_cell_menu && + !(event.target as HTMLElement).closest(".cell-menu") + ) { + active_cell_menu = null; + } + event.stopImmediatePropagation(); const [trigger] = event.composedPath() as HTMLElement[]; if (parent.contains(trigger)) { @@ -463,6 +476,7 @@ header_edit = false; selected_header = false; selected = false; + active_cell = null; } function guess_delimitaor( @@ -640,6 +654,56 @@ observer.disconnect(); }; }); + + let highlighted_column: number | null = null; + + let active_cell_menu: { + row: number; + col: number; + x: number; + y: number; + } | null = null; + + function toggle_cell_menu(event: MouseEvent, row: number, col: number): void { + event.stopPropagation(); + if ( + active_cell_menu && + active_cell_menu.row === row && + active_cell_menu.col === col + ) { + active_cell_menu = null; + } else { + const cell = (event.target as HTMLElement).closest("td"); + if (cell) { + const rect = cell.getBoundingClientRect(); + active_cell_menu = { + row, + col, + x: rect.right, + y: rect.bottom + }; + } + } + } + + function add_row_at(index: number, position: "above" | "below"): void { + const row_index = position === "above" ? index : index + 1; + add_row(row_index); + active_cell_menu = null; + } + + function add_col_at(index: number, position: "left" | "right"): void { + const col_index = position === "left" ? index : index + 1; + add_col(col_index); + active_cell_menu = null; + } + + onMount(() => { + document.addEventListener("click", handle_click_outside); + return () => { + document.removeEventListener("click", handle_click_outside); + }; + });
+ {#if editable} + + {/if}
{/each} @@ -829,64 +907,22 @@ - {#if editable} -
- {#if row_count[1] === "dynamic"} - - (e.stopPropagation(), add_row())} - > - - {i18n("dataframe.new_row")} - - - {/if} - {#if col_count[1] === "dynamic"} - - (e.stopPropagation(), add_col())} - > - - {i18n("dataframe.new_column")} - - - {/if} -
- {/if} +{#if active_cell_menu !== null} + add_row_at(active_cell_menu?.row ?? -1, "above")} + on_add_row_below={() => add_row_at(active_cell_menu?.row ?? -1, "below")} + on_add_column_left={() => add_col_at(active_cell_menu?.col ?? -1, "left")} + on_add_column_right={() => add_col_at(active_cell_menu?.col ?? -1, "right")} + /> +{/if} +