Skip to content

Commit

Permalink
Support column/row deletion in gr.DataFrame (#10420)
Browse files Browse the repository at this point in the history
* support deletion

* changes

* changes

* add changeset

* update icons

* add changeset

* notebook

* changes

* simplify

* fix icons

* changes

* changes

* change

* revert

* flaky test

---------

Co-authored-by: gradio-pr-bot <[email protected]>
  • Loading branch information
abidlabs and gradio-pr-bot authored Jan 29, 2025
1 parent 324383f commit a69b8e8
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 35 deletions.
7 changes: 7 additions & 0 deletions .changeset/few-items-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@gradio/core": patch
"@gradio/dataframe": patch
"gradio": patch
---

feat:Support column/row deletion in `gr.DataFrame`
1 change: 1 addition & 0 deletions client/python/test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,7 @@ def __call__(self, *args, **kwargs):

assert all(s in messages for s in statuses)

@pytest.mark.flaky
@patch("gradio_client.client.Endpoint.make_end_to_end_fn")
def test_messages_correct_two_concurrent(
self, mock_make_end_to_end_fn, calculator_demo
Expand Down
4 changes: 2 additions & 2 deletions gradio/components/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ def __init__(
Parameters:
value: Default value to display in the DataFrame. If a Styler is provided, it will be used to set the displayed value in the DataFrame (e.g. to set precision of numbers) if the `interactive` is False. If a Callable function is provided, the function will be called whenever the app loads to set the initial value of the component.
headers: List of str header names. If None, no headers are shown.
row_count: Limit number of rows for input and decide whether user can create new rows. The first element of the tuple is an `int`, the row count; the second should be 'fixed' or 'dynamic', the new row behaviour. If an `int` is passed the rows default to 'dynamic'
col_count: Limit number of columns for input and decide whether user can create new columns. The first element of the tuple is an `int`, the number of columns; the second should be 'fixed' or 'dynamic', the new column behaviour. If an `int` is passed the columns default to 'dynamic'
row_count: Limit number of rows for input and decide whether user can create new rows or delete existing rows. The first element of the tuple is an `int`, the row count; the second should be 'fixed' or 'dynamic', the new row behaviour. If an `int` is passed the rows default to 'dynamic'
col_count: Limit number of columns for input and decide whether user can create new columns or delete existing columns. The first element of the tuple is an `int`, the number of columns; the second should be 'fixed' or 'dynamic', the new column behaviour. If an `int` is passed the columns default to 'dynamic'
datatype: Datatype of values in sheet. Can be provided per column as a list of strings, or for the entire sheet as a single string. Valid datatypes are "str", "number", "bool", "date", and "markdown".
type: Type of value to be returned by component. "pandas" for pandas dataframe, "numpy" for numpy array, "polars" for polars dataframe, or "array" for a Python list of lists.
label: the label for this component. Appears above the component and is also used as the header if there are a table of examples for this component. If None and used in a `gr.Interface`, the label will be the name of the parameter this component is assigned to.
Expand Down
2 changes: 2 additions & 0 deletions js/core/src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
"new_row": "New row",
"add_row_above": "Add row above",
"add_row_below": "Add row below",
"delete_row": "Delete row",
"delete_column": "Delete column",
"add_column_left": "Add column to the left",
"add_column_right": "Add column to the right"
},
Expand Down
10 changes: 0 additions & 10 deletions js/dataframe/shared/Arrow.svelte

This file was deleted.

30 changes: 21 additions & 9 deletions js/dataframe/shared/CellMenu.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from "svelte";
import Arrow from "./Arrow.svelte";
import CellMenuIcons from "./CellMenuIcons.svelte";
import type { I18nFormatter } from "js/utils/src";
export let x: number;
Expand All @@ -12,6 +12,10 @@
export let row: number;
export let col_count: [number, "fixed" | "dynamic"];
export let row_count: [number, "fixed" | "dynamic"];
export let on_delete_row: () => void;
export let on_delete_col: () => void;
export let can_delete_rows: boolean;
export let can_delete_cols: boolean;
export let i18n: I18nFormatter;
let menu_element: HTMLDivElement;
Expand Down Expand Up @@ -50,23 +54,35 @@
<div bind:this={menu_element} class="cell-menu">
{#if !is_header && can_add_rows}
<button on:click={() => on_add_row_above()}>
<Arrow transform="rotate(-90 12 12)" />
<CellMenuIcons icon="add-row-above" />
{i18n("dataframe.add_row_above")}
</button>
<button on:click={() => on_add_row_below()}>
<Arrow transform="rotate(90 12 12)" />
<CellMenuIcons icon="add-row-below" />
{i18n("dataframe.add_row_below")}
</button>
{#if can_delete_rows}
<button on:click={on_delete_row} class="delete">
<CellMenuIcons icon="delete-row" />
{i18n("dataframe.delete_row")}
</button>
{/if}
{/if}
{#if can_add_columns}
<button on:click={() => on_add_column_left()}>
<Arrow transform="rotate(180 12 12)" />
<CellMenuIcons icon="add-column-left" />
{i18n("dataframe.add_column_left")}
</button>
<button on:click={() => on_add_column_right()}>
<Arrow transform="rotate(0 12 12)" />
<CellMenuIcons icon="add-column-right" />
{i18n("dataframe.add_column_right")}
</button>
{#if can_delete_cols}
<button on:click={on_delete_col} class="delete">
<CellMenuIcons icon="delete-column" />
{i18n("dataframe.delete_column")}
</button>
{/if}
{/if}
</div>

Expand Down Expand Up @@ -110,8 +126,4 @@
fill: currentColor;
transition: fill 0.2s;
}
.cell-menu button:hover :global(svg) {
fill: var(--color-accent);
}
</style>
113 changes: 113 additions & 0 deletions js/dataframe/shared/CellMenuIcons.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<script lang="ts">
export let icon: string;
</script>

{#if icon == "add-column-right"}
<svg viewBox="0 0 24 24" width="16" height="16">
<rect
x="4"
y="6"
width="4"
height="12"
stroke="currentColor"
stroke-width="2"
fill="none"
/>
<path
d="M12 12H19M16 8L19 12L16 16"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
/>
</svg>
{:else if icon == "add-column-left"}
<svg viewBox="0 0 24 24" width="16" height="16">
<rect
x="16"
y="6"
width="4"
height="12"
stroke="currentColor"
stroke-width="2"
fill="none"
/>
<path
d="M12 12H5M8 8L5 12L8 16"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
/>
</svg>
{:else if icon == "add-row-above"}
<svg viewBox="0 0 24 24" width="16" height="16">
<rect
x="6"
y="16"
width="12"
height="4"
stroke="currentColor"
stroke-width="2"
/>
<path
d="M12 12V5M8 8L12 5L16 8"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
/>
</svg>
{:else if icon == "add-row-below"}
<svg viewBox="0 0 24 24" width="16" height="16">
<rect
x="6"
y="4"
width="12"
height="4"
stroke="currentColor"
stroke-width="2"
/>
<path
d="M12 12V19M8 16L12 19L16 16"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
/>
</svg>
{:else if icon == "delete-row"}
<svg viewBox="0 0 24 24" width="16" height="16">
<rect
x="5"
y="10"
width="14"
height="4"
stroke="currentColor"
stroke-width="2"
/>
<path
d="M8 7L16 17M16 7L8 17"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
{:else if icon == "delete-column"}
<svg viewBox="0 0 24 24" width="16" height="16">
<rect
x="10"
y="5"
width="4"
height="14"
stroke="currentColor"
stroke-width="2"
/>
<path
d="M7 8L17 16M17 8L7 16"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
{/if}
66 changes: 52 additions & 14 deletions js/dataframe/shared/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,7 @@
id: string;
}[][] {
const data_row_length = _values.length;
return Array(
row_count[1] === "fixed"
? row_count[0]
: data_row_length < row_count[0]
? row_count[0]
: data_row_length
)
return Array(row_count[1] === "fixed" ? row_count[0] : data_row_length)
.fill(0)
.map((_, i) =>
Array(
Expand Down Expand Up @@ -791,6 +785,42 @@
afterUpdate(() => {
value_is_output = false;
});
async function delete_row(index: number): Promise<void> {
parent.focus();
if (row_count[1] !== "dynamic") return;
if (data.length <= 1) return;
data.splice(index, 1);
data = data;
selected = false;
}
async function delete_col(index: number): Promise<void> {
parent.focus();
if (col_count[1] !== "dynamic") return;
if (data[0].length <= 1) return;
_headers.splice(index, 1);
_headers = _headers;
data.forEach((row) => {
row.splice(index, 1);
});
data = data;
selected = false;
}
function delete_row_at(index: number): void {
delete_row(index);
active_cell_menu = null;
active_header_menu = null;
}
function delete_col_at(index: number): void {
delete_col(index);
active_cell_menu = null;
active_header_menu = null;
}
</script>

<svelte:window on:resize={() => set_cell_widths()} />
Expand Down Expand Up @@ -1050,18 +1080,22 @@
</div>
</div>

{#if active_cell_menu !== null}
{#if active_cell_menu}
<CellMenu
{i18n}
x={active_cell_menu.x}
y={active_cell_menu.y}
row={active_cell_menu?.row ?? -1}
row={active_cell_menu.row}
{col_count}
{row_count}
on_add_row_above={() => 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")}
on_add_row_above={() => add_row_at(active_cell_menu?.row || 0, "above")}
on_add_row_below={() => add_row_at(active_cell_menu?.row || 0, "below")}
on_add_column_left={() => add_col_at(active_cell_menu?.col || 0, "left")}
on_add_column_right={() => add_col_at(active_cell_menu?.col || 0, "right")}
on_delete_row={() => delete_row_at(active_cell_menu?.row || 0)}
on_delete_col={() => delete_col_at(active_cell_menu?.col || 0)}
can_delete_rows={data.length > 1}
can_delete_cols={data[0].length > 1}
{i18n}
/>
{/if}

Expand All @@ -1078,6 +1112,10 @@
on_add_column_left={() => add_col_at(active_header_menu?.col ?? -1, "left")}
on_add_column_right={() =>
add_col_at(active_header_menu?.col ?? -1, "right")}
on_delete_row={() => delete_row_at(active_cell_menu?.row ?? -1)}
on_delete_col={() => delete_col_at(active_header_menu?.col ?? -1)}
can_delete_rows={false}
can_delete_cols={data[0].length > 1}
/>
{/if}

Expand Down

0 comments on commit a69b8e8

Please sign in to comment.