diff --git a/.changeset/smooth-humans-flash.md b/.changeset/smooth-humans-flash.md new file mode 100644 index 000000000000..be54a9cfd83e --- /dev/null +++ b/.changeset/smooth-humans-flash.md @@ -0,0 +1,6 @@ +--- +"@gradio/form": patch +"gradio": patch +--- + +fix:`gr.Dropdown()` now supports values with arbitrary characters and doesn't clear value when re-focused diff --git a/gradio/components/dropdown.py b/gradio/components/dropdown.py index 473e105268e8..bc15fc990550 100644 --- a/gradio/components/dropdown.py +++ b/gradio/components/dropdown.py @@ -46,6 +46,7 @@ def __init__( value: str | list[str] | Callable | None = None, type: Literal["value", "index"] = "value", multiselect: bool | None = None, + allow_custom_value: bool = False, max_choices: int | None = None, label: str | None = None, info: str | None = None, @@ -58,7 +59,6 @@ def __init__( visible: bool = True, elem_id: str | None = None, elem_classes: list[str] | str | None = None, - allow_custom_value: bool = False, **kwargs, ): """ @@ -67,6 +67,7 @@ def __init__( 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. + allow_custom_value: If True, allows user to enter a custom value that is not in the list of choices. Only applies if `multiselect` is False. 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. @@ -79,7 +80,6 @@ def __init__( visible: If False, component will be hidden. elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles. elem_classes: An optional list of strings that are assigned as the classes of this component in the HTML DOM. Can be used for targeting CSS styles. - allow_custom_value: If True, allows user to enter a custom value that is not in the list of choices. """ self.choices = [str(choice) for choice in choices] if choices else [] valid_types = ["value", "index"] diff --git a/js/form/src/Dropdown.svelte b/js/form/src/Dropdown.svelte index fec3851f9daa..e78de1a5bb9d 100644 --- a/js/form/src/Dropdown.svelte +++ b/js/form/src/Dropdown.svelte @@ -6,19 +6,19 @@ import type { SelectData } from "@gradio/utils"; export let label: string; export let info: string | undefined = undefined; - export let value: string | Array | undefined; + export let value: string | string[] | undefined; let old_value = Array.isArray(value) ? value.slice() : value; - export let value_is_output: boolean = false; - export let multiselect: boolean = false; + export let value_is_output = false; + export let multiselect = false; export let max_choices: number; - export let choices: Array; - export let disabled: boolean = false; + export let choices: string[]; + export let disabled = false; export let show_label: boolean; - export let container: boolean = true; - export let allow_custom_value: boolean = false; + export let container = true; + export let allow_custom_value = false; const dispatch = createEventDispatcher<{ - change: string | Array | undefined; + change: string | string[] | undefined; input: undefined; select: SelectData; blur: undefined; @@ -41,7 +41,7 @@ activeOption = filtered.length ? filtered[0] : null; } - function handle_change() { + function handle_change(): void { dispatch("change", value); if (!value_is_output) { dispatch("input"); @@ -57,8 +57,8 @@ } } - function add(option: string) { - value = value as Array; + function add(option: string): void { + value = value as string[]; if (!max_choices || value.length < max_choices) { value.push(option); dispatch("select", { @@ -70,8 +70,8 @@ value = value; } - function remove(option: string) { - value = value as Array; + function remove(option: string): void { + value = value as string[]; value = value.filter((v: string) => v !== option); dispatch("select", { index: choices.indexOf(option), @@ -80,13 +80,13 @@ }); } - function remove_all(e: any) { + function remove_all(e: any): void { value = []; inputValue = ""; e.preventDefault(); } - function handleOptionMousedown(e: any) { + function handleOptionMousedown(e: any): void { const option = e.detail.target.dataset.value; if (allow_custom_value) { inputValue = option; @@ -109,11 +109,19 @@ value: option, selected: true }); - return; } } } + function handleFocus(): void { + showOptions = !showOptions; + if (showOptions) { + filtered = choices; + } else { + filterInput.blur(); + } + } + function handleKeydown(e: any) { if (e.key === "Enter" && activeOption != undefined) { if (!multiselect) { @@ -200,14 +208,7 @@ autocomplete="off" bind:value={inputValue} bind:this={filterInput} - on:focus={() => { - showOptions = !showOptions; - if (showOptions) { - inputValue = ""; - } else { - filterInput.blur(); - } - }} + on:focus={handleFocus} on:keydown={handleKeydown} on:keyup={() => { if (allow_custom_value) { diff --git a/js/form/src/DropdownOptions.svelte b/js/form/src/DropdownOptions.svelte index 32ae3072787c..93a696a5fdea 100644 --- a/js/form/src/DropdownOptions.svelte +++ b/js/form/src/DropdownOptions.svelte @@ -1,11 +1,11 @@