mirror of
https://github.com/gradio-app/gradio.git
synced 2025-03-25 12:10:31 +08:00
Dropdown Component Updates (#3211)
* dropdown * more dropdown updates * dropdown styling + option visibility * changelog * notebook * fix test * Allow more image formats (#3225) * add wildcard to image input * simplify mime types * changelog * regen noteboks --------- Co-authored-by: Abubakar Abid <abubakar@huggingface.co> Co-authored-by: pngwn <hello@pngwn.io> * fix webcam mirroring (#3245) * fix webcam * changelog * fix changelog * fix changelog * fix changelog --------- Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * Add `interactive=False` mode to `gr.Button` (#3266) * add interactive=False to button * add interactive=True by default * changelog * fix frontend * fix backend test * formatting * review changes * LaTeX height fix (#3258) * latex height fix * changelog * formatting * em * em * accidentally added script (#3273) * Adding a script to benchmark the queue (#3272) * added benchmark queue script * changelg * fix instructions * Fix matplotlib image size (#3274) * Fix matplotlib css * CHANGELOG * Undo lockfile * Add timeouts to queue messages (#3196) * Fix + test * Remove print statements + fix import for 3.7 * CHANGELOG * Remove more print statements * Add 60 second timeout for uploading data * Fix test --------- Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * icons * separate options into component * formatting * changelog * changelog * fix ui tests * formatting again... * backend test fix * format * doc fixes --------- Co-authored-by: Abubakar Abid <abubakar@huggingface.co> Co-authored-by: fienestar <fienestar@gmail.com> Co-authored-by: pngwn <hello@pngwn.io> Co-authored-by: Freddy Boulton <alfonsoboulton@gmail.com>
This commit is contained in:
parent
ed33e8f1a8
commit
f36211050c
@ -2,6 +2,15 @@
|
||||
|
||||
## New Features:
|
||||
|
||||
### Dropdown Component Updates
|
||||
|
||||
The standard dropdown component now supports searching for choices. Also when `multiselect` is `True`, you can specify `max_choices` to set the maximum number of choices you want the user to be able to select from the dropdown component.
|
||||
|
||||
```python
|
||||
gr.Dropdown(label="Choose your favorite colors", choices=["red", "blue", "green", "yellow", "orange"], multiselect=True, max_choices=2)
|
||||
```
|
||||
by [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3211](https://github.com/gradio-app/gradio/pull/3211)
|
||||
|
||||
### Download button for images 🖼️
|
||||
|
||||
Output images will now automatically have a download button displayed to make it easier to save and share
|
||||
|
File diff suppressed because one or more lines are too long
@ -19,6 +19,7 @@ def fn(
|
||||
checkboxes,
|
||||
radio,
|
||||
dropdown,
|
||||
multi_dropdown,
|
||||
im1,
|
||||
im2,
|
||||
im3,
|
||||
@ -96,6 +97,7 @@ demo = gr.Interface(
|
||||
gr.CheckboxGroup(label="CheckboxGroup", choices=CHOICES, value=CHOICES[0:2]),
|
||||
gr.Radio(label="Radio", choices=CHOICES, value=CHOICES[2]),
|
||||
gr.Dropdown(label="Dropdown", choices=CHOICES),
|
||||
gr.Dropdown(label="Multiselect Dropdown (Max choice: 2)", choices=CHOICES, multiselect=True, max_choices=2),
|
||||
gr.Image(label="Image"),
|
||||
gr.Image(label="Image w/ Cropper", tool="select"),
|
||||
gr.Image(label="Sketchpad", source="canvas"),
|
||||
@ -135,6 +137,7 @@ demo = gr.Interface(
|
||||
["foo", "baz"],
|
||||
"baz",
|
||||
"bar",
|
||||
["foo", "bar"],
|
||||
os.path.join(os.path.dirname(__file__), "files/cheetah1.jpg"),
|
||||
os.path.join(os.path.dirname(__file__), "files/cheetah1.jpg"),
|
||||
os.path.join(os.path.dirname(__file__), "files/cheetah1.jpg"),
|
||||
|
@ -1204,6 +1204,7 @@ class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
value: str | List[str] | Callable | None = None,
|
||||
type: str = "value",
|
||||
multiselect: bool | None = None,
|
||||
max_choices: int | None = None,
|
||||
label: str | None = None,
|
||||
info: str | None = None,
|
||||
every: float | None = None,
|
||||
@ -1219,6 +1220,7 @@ class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
value: default value(s) selected in dropdown. If None, no value is selected by default. If callable, the function will be called whenever the app loads to set the initial value of the component.
|
||||
type: Type of value to be returned by component. "value" returns the string of the choice selected, "index" returns the index of the choice selected.
|
||||
multiselect: if True, multiple choices can be selected.
|
||||
max_choices: maximum number of choices that can be selected. If None, no limit is enforced.
|
||||
label: component name in interface.
|
||||
info: additional component description.
|
||||
every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. Queue must be enabled. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute.
|
||||
@ -1238,6 +1240,9 @@ class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
if multiselect:
|
||||
if isinstance(value, str):
|
||||
value = [value]
|
||||
if not multiselect and max_choices is not None:
|
||||
warnings.warn("The `max_choices` parameter is ignored when `multiselect` is False.")
|
||||
self.max_choices = max_choices
|
||||
self.test_input = self.choices[0] if len(self.choices) else None
|
||||
self.interpret_by_tokens = False
|
||||
IOComponent.__init__(
|
||||
@ -1259,6 +1264,7 @@ class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
"choices": self.choices,
|
||||
"value": self.value,
|
||||
"multiselect": self.multiselect,
|
||||
"max_choices": self.max_choices,
|
||||
**IOComponent.get_config(self),
|
||||
}
|
||||
|
||||
|
@ -563,7 +563,7 @@ class TestDropdown:
|
||||
assert dropdown_input.preprocess("a") == "a"
|
||||
assert dropdown_input.postprocess("a") == "a"
|
||||
|
||||
dropdown_input_multiselect = gr.Dropdown(["a", "b", "c"], multiselect=True)
|
||||
dropdown_input_multiselect = gr.Dropdown(["a", "b", "c"])
|
||||
assert dropdown_input_multiselect.preprocess(["a", "c"]) == ["a", "c"]
|
||||
assert dropdown_input_multiselect.postprocess(["a", "c"]) == ["a", "c"]
|
||||
assert dropdown_input_multiselect.serialize(["a", "c"], True) == ["a", "c"]
|
||||
@ -572,6 +572,8 @@ class TestDropdown:
|
||||
value=["a", "c"],
|
||||
choices=["a", "b", "c"],
|
||||
label="Select Your Inputs",
|
||||
multiselect=True,
|
||||
max_choices=2,
|
||||
)
|
||||
assert dropdown_input_multiselect.get_config() == {
|
||||
"choices": ["a", "b", "c"],
|
||||
@ -584,7 +586,8 @@ class TestDropdown:
|
||||
"visible": True,
|
||||
"interactive": None,
|
||||
"root_url": None,
|
||||
"multiselect": None,
|
||||
"multiselect": True,
|
||||
"max_choices": 2,
|
||||
}
|
||||
with pytest.raises(ValueError):
|
||||
gr.Dropdown(["a"], type="unknown")
|
||||
|
@ -11,6 +11,7 @@
|
||||
export let visible: boolean = true;
|
||||
export let value: string | Array<string> = [];
|
||||
export let multiselect: boolean = false;
|
||||
export let max_choices: number;
|
||||
export let choices: Array<string>;
|
||||
export let show_label: boolean;
|
||||
export let style: Styles = {};
|
||||
@ -30,6 +31,7 @@
|
||||
bind:value
|
||||
{choices}
|
||||
{multiselect}
|
||||
{max_choices}
|
||||
{label}
|
||||
{info}
|
||||
{show_label}
|
||||
|
@ -31,8 +31,10 @@ test("matplotlib", async ({ page }) => {
|
||||
await mock_api(page, [[{ type: "matplotlib", plot: BASE64_PLOT_IMG }]]);
|
||||
await page.goto("http://localhost:9876");
|
||||
|
||||
await page.getByLabel("Plot Type").selectOption("Matplotlib");
|
||||
await page.getByLabel("Month").selectOption("January");
|
||||
await page.getByLabel("Plot Type").click();
|
||||
await page.getByRole("button", { name: "Matplotlib" }).click();
|
||||
await page.getByLabel("Month").click();
|
||||
await page.getByRole("button", { name: "January" }).click();
|
||||
await page.getByLabel("Social Distancing?").check();
|
||||
|
||||
await Promise.all([
|
||||
@ -57,8 +59,10 @@ test("plotly", async ({ page }) => {
|
||||
]);
|
||||
await page.goto("http://localhost:9876");
|
||||
|
||||
await page.getByLabel("Plot Type").selectOption("Plotly");
|
||||
await page.getByLabel("Month").selectOption("January");
|
||||
await page.getByLabel("Plot Type").click();
|
||||
await page.getByRole("button", { name: "Matplotlib" }).click();
|
||||
await page.getByLabel("Month").click();
|
||||
await page.getByRole("button", { name: "January" }).click();
|
||||
await page.getByLabel("Social Distancing?").check();
|
||||
|
||||
await Promise.all([
|
||||
|
@ -9,6 +9,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/atoms": "workspace:^0.0.1",
|
||||
"@gradio/utils": "workspace:^0.0.1"
|
||||
"@gradio/utils": "workspace:^0.0.1",
|
||||
"@gradio/icons": "workspace:^0.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -1,70 +1,270 @@
|
||||
<script lang="ts">
|
||||
import MultiSelect from "./MultiSelect.svelte";
|
||||
import DropdownOptions from "./DropdownOptions.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { BlockTitle } from "@gradio/atoms";
|
||||
import { Remove, DropdownArrow } from "@gradio/icons";
|
||||
export let label: string;
|
||||
export let info: string | undefined = undefined;
|
||||
export let value: string | Array<string> | undefined = undefined;
|
||||
export let multiselect: boolean = false;
|
||||
export let max_choices: number;
|
||||
export let choices: Array<string>;
|
||||
export let disabled: boolean = false;
|
||||
export let show_label: boolean;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: string | Array<string>;
|
||||
change: string | Array<string> | undefined;
|
||||
}>();
|
||||
|
||||
let inputValue: string,
|
||||
activeOption: string,
|
||||
showOptions = false;
|
||||
|
||||
$: filtered = choices.filter((o) =>
|
||||
inputValue ? o.toLowerCase().includes(inputValue.toLowerCase()) : o
|
||||
);
|
||||
$: if (
|
||||
(activeOption && !filtered.includes(activeOption)) ||
|
||||
(!activeOption && inputValue)
|
||||
)
|
||||
activeOption = filtered[0];
|
||||
|
||||
$: readonly =
|
||||
(!multiselect && typeof value === "string") ||
|
||||
(multiselect && Array.isArray(value) && value.length === max_choices);
|
||||
|
||||
// The initial value of value is [] so that can
|
||||
// cause infinite loops in the non-multiselect case
|
||||
$: if (!multiselect && !Array.isArray(value)) {
|
||||
dispatch("change", value);
|
||||
}
|
||||
|
||||
function add(option: string) {
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length < max_choices) {
|
||||
value.push(option);
|
||||
dispatch("change", value);
|
||||
}
|
||||
showOptions = !(value.length === max_choices);
|
||||
}
|
||||
value = value;
|
||||
}
|
||||
|
||||
function remove(option: string) {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.filter((v: string) => v !== option);
|
||||
dispatch("change", value);
|
||||
}
|
||||
}
|
||||
|
||||
function remove_all(e: any) {
|
||||
value = [];
|
||||
inputValue = "";
|
||||
e.preventDefault();
|
||||
dispatch("change", value);
|
||||
}
|
||||
|
||||
function handleOptionMousedown(e: any) {
|
||||
const option = e.detail.target.dataset.value;
|
||||
inputValue = "";
|
||||
|
||||
if (option !== undefined) {
|
||||
if (!multiselect) {
|
||||
value = option;
|
||||
inputValue = "";
|
||||
dispatch("change", value);
|
||||
return;
|
||||
}
|
||||
if (value?.includes(option)) {
|
||||
remove(option);
|
||||
} else {
|
||||
add(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyup(e: any) {
|
||||
if (e.key === "Enter" && activeOption != undefined) {
|
||||
if (!multiselect) {
|
||||
value = activeOption;
|
||||
inputValue = "";
|
||||
} else if (multiselect && Array.isArray(value)) {
|
||||
value.includes(activeOption) ? remove(activeOption) : add(activeOption);
|
||||
inputValue = "";
|
||||
}
|
||||
}
|
||||
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||
const increment = e.key === "ArrowUp" ? -1 : 1;
|
||||
const calcIndex = filtered.indexOf(activeOption) + increment;
|
||||
activeOption =
|
||||
calcIndex < 0
|
||||
? filtered[filtered.length - 1]
|
||||
: calcIndex === filtered.length
|
||||
? filtered[0]
|
||||
: filtered[calcIndex];
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
showOptions = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
<label>
|
||||
<BlockTitle {show_label} {info}>{label}</BlockTitle>
|
||||
|
||||
{#if !multiselect}
|
||||
<select bind:value {disabled}>
|
||||
{#each choices as choice}
|
||||
<option>{choice}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<MultiSelect bind:value {choices} on:change {disabled} />
|
||||
{/if}
|
||||
<div class="wrap">
|
||||
<div class="wrap-inner" class:showOptions>
|
||||
{#if Array.isArray(value)}
|
||||
{#each value as s}
|
||||
<div on:click|preventDefault={() => remove(s)} class="token">
|
||||
<span>{s}</span>
|
||||
<div
|
||||
class:hidden={disabled}
|
||||
class="token-remove"
|
||||
title="Remove {s}"
|
||||
>
|
||||
<Remove />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<span class="single-select">{value}</span>
|
||||
{/if}
|
||||
<div class="secondary-wrap">
|
||||
<input
|
||||
class="border-none"
|
||||
{disabled}
|
||||
{readonly}
|
||||
autocomplete="off"
|
||||
bind:value={inputValue}
|
||||
on:focus={() =>
|
||||
(showOptions =
|
||||
Array.isArray(value) && value.length === max_choices
|
||||
? false
|
||||
: true)}
|
||||
on:blur={() => (showOptions = false)}
|
||||
on:keyup={handleKeyup}
|
||||
/>
|
||||
<div
|
||||
class:hide={!value?.length || disabled}
|
||||
class="token-remove remove-all"
|
||||
title="Remove All"
|
||||
on:click={remove_all}
|
||||
>
|
||||
<Remove />
|
||||
</div>
|
||||
<DropdownArrow />
|
||||
</div>
|
||||
</div>
|
||||
<DropdownOptions
|
||||
bind:value
|
||||
{showOptions}
|
||||
{filtered}
|
||||
{activeOption}
|
||||
{disabled}
|
||||
on:change={handleOptionMousedown}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<style>
|
||||
select {
|
||||
.wrap {
|
||||
--ring-color: transparent;
|
||||
display: block;
|
||||
position: relative;
|
||||
outline: none !important;
|
||||
box-shadow: 0 0 0 var(--shadow-spread) var(--ring-color),
|
||||
var(--shadow-inset);
|
||||
border: 1px solid var(--input-border-color-base);
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-lg);
|
||||
background-color: var(--input-background-base);
|
||||
padding: var(--size-2-5);
|
||||
width: 100%;
|
||||
color: var(--color-text-body);
|
||||
font-size: var(--scale-00);
|
||||
line-height: var(--line-sm);
|
||||
}
|
||||
|
||||
select:focus {
|
||||
.wrap:focus-within {
|
||||
--ring-color: var(--color-focus-ring);
|
||||
border-color: var(--input-border-color-focus);
|
||||
}
|
||||
|
||||
select::placeholder {
|
||||
color: var(--color-text-placeholder);
|
||||
.wrap-inner {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
select[disabled] {
|
||||
.token {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin: var(--size-1);
|
||||
box-shadow: var(--shadow-drop);
|
||||
border: 1px solid var(--checkbox-label-border-color-base);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--checkbox-label-background-base);
|
||||
padding: var(--size-1-5) var(--size-3);
|
||||
color: var(--color-text-body);
|
||||
font-size: var(--scale-00);
|
||||
line-height: var(--line-md);
|
||||
}
|
||||
|
||||
.token > * + * {
|
||||
margin-left: var(--size-2);
|
||||
}
|
||||
|
||||
.token:hover {
|
||||
border: 1px solid var(--icon_button-border-color-hover);
|
||||
color: var(--color-text-label);
|
||||
}
|
||||
|
||||
.token-remove {
|
||||
fill: var(--color-text-body);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: var(--radius-full);
|
||||
background: var(--color-background-tertiary);
|
||||
padding: var(--size-0-5);
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.token-remove:hover,
|
||||
.remove-all:hover {
|
||||
border: 1px solid var(--icon_button-border-color-hover);
|
||||
color: var(--color-text-label);
|
||||
}
|
||||
|
||||
.single-select {
|
||||
margin: var(--size-2);
|
||||
color: var(--color-text-body);
|
||||
}
|
||||
|
||||
.secondary-wrap {
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
align-items: center;
|
||||
border: none;
|
||||
min-width: min-content;
|
||||
}
|
||||
|
||||
input {
|
||||
outline: none;
|
||||
border: none;
|
||||
background: inherit;
|
||||
padding: var(--size-2-5);
|
||||
width: 100%;
|
||||
color: var(--color-text-body);
|
||||
font-size: var(--scale-00);
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.remove-all {
|
||||
margin-left: var(--size-1);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
|
73
ui/packages/form/src/DropdownOptions.svelte
Normal file
73
ui/packages/form/src/DropdownOptions.svelte
Normal file
@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
import { fly } from "svelte/transition";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let value: string | Array<string> | undefined = undefined;
|
||||
export let filtered: Array<string>;
|
||||
export let showOptions: boolean = false;
|
||||
export let activeOption: string;
|
||||
export let disabled: boolean = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
{#if showOptions && !disabled}
|
||||
<ul
|
||||
class="options"
|
||||
aria-expanded={showOptions}
|
||||
transition:fly={{ duration: 200, y: 5 }}
|
||||
on:mousedown|preventDefault={(e) => dispatch("change", e)}
|
||||
>
|
||||
{#each filtered as choice}
|
||||
<li
|
||||
class="item"
|
||||
role="button"
|
||||
class:selected={value?.includes(choice)}
|
||||
class:active={activeOption === choice}
|
||||
class:bg-gray-100={activeOption === choice}
|
||||
class:dark:bg-gray-600={activeOption === choice}
|
||||
data-value={choice}
|
||||
aria-label={choice}
|
||||
>
|
||||
<span class:hide={!value?.includes(choice)} class="inner-item pr-1">
|
||||
✓
|
||||
</span>
|
||||
{choice}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.options {
|
||||
position: absolute;
|
||||
z-index: var(--layer-5);
|
||||
margin-left: 0;
|
||||
box-shadow: var(--shadow-drop-lg);
|
||||
border-radius: var(--radius-lg);
|
||||
background: var(--color-background-primary);
|
||||
width: var(--size-full);
|
||||
max-height: var(--size-32);
|
||||
overflow: auto;
|
||||
color: var(--color-text-body);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
padding: var(--size-2);
|
||||
}
|
||||
|
||||
.item:hover,
|
||||
.active {
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
|
||||
.inner-item {
|
||||
padding-right: var(--size-1);
|
||||
}
|
||||
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
17
ui/packages/icons/src/DropdownArrow.svelte
Normal file
17
ui/packages/icons/src/DropdownArrow.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<svg
|
||||
class="dropdown-arrow"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
>
|
||||
<path d="M5 8l4 4 4-4z" />
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.dropdown-arrow {
|
||||
fill: var(--color-text-body);
|
||||
margin-right: var(--size-2);
|
||||
width: var(--size-5);
|
||||
}
|
||||
</style>
|
After Width: | Height: | Size: 275 B |
10
ui/packages/icons/src/Remove.svelte
Normal file
10
ui/packages/icons/src/Remove.svelte
Normal file
@ -0,0 +1,10 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 215 B |
@ -5,6 +5,7 @@ export { default as Chat } from "./Chat.svelte";
|
||||
export { default as Circle } from "./Circle.svelte";
|
||||
export { default as Clear } from "./Clear.svelte";
|
||||
export { default as Color } from "./Color.svelte";
|
||||
export { default as DropdownArrow } from "./DropdownArrow.svelte";
|
||||
export { default as Edit } from "./Edit.svelte";
|
||||
export { default as File } from "./File.svelte";
|
||||
export { default as Image } from "./Image.svelte";
|
||||
@ -15,6 +16,7 @@ export { default as Music } from "./Music.svelte";
|
||||
export { default as Pause } from "./Pause.svelte";
|
||||
export { default as Play } from "./Play.svelte";
|
||||
export { default as Plot } from "./Plot.svelte";
|
||||
export { default as Remove } from "./Remove.svelte";
|
||||
export { default as Sketch } from "./Sketch.svelte";
|
||||
export { default as Square } from "./Square.svelte";
|
||||
export { default as Table } from "./Table.svelte";
|
||||
|
2
ui/pnpm-lock.yaml
generated
2
ui/pnpm-lock.yaml
generated
@ -230,9 +230,11 @@ importers:
|
||||
packages/form:
|
||||
specifiers:
|
||||
'@gradio/atoms': workspace:^0.0.1
|
||||
'@gradio/icons': workspace:^0.0.1
|
||||
'@gradio/utils': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/atoms': link:../atoms
|
||||
'@gradio/icons': link:../icons
|
||||
'@gradio/utils': link:../utils
|
||||
|
||||
packages/gallery:
|
||||
|
Loading…
x
Reference in New Issue
Block a user