Repository: gradio-app/gradio
Pull request: Adds `variant` and `interactive` props to `gr.UploadButton`
Closes: #4364 

Test code:

```py
import gradio as gr

with gr.Blocks() as demo:
    gr.Button()
    gr.Button(interactive=False)
    gr.Button(variant="primary")
    gr.Button(variant="stop", interactive=False)
    gr.UploadButton()
    gr.UploadButton(interactive=False)
    gr.UploadButton(variant="primary")
    gr.UploadButton(variant="stop", interactive=False)
```

<img width="1292" alt="image" src="https://github.com/gradio-app/gradio/assets/1778297/9eabb6cf-d6d4-4e98-a6a3-d9ca0f2fdfd0">


Problem statement:
UploadButton cannot be made non-interactive
### Describe the bug

The UploadButton can be made non-interactive but I can imagine a use case where this is desired: While a function is running prevent the user from uploading another file.



### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

`gr.UploadButton(interactive=False)` will still let you upload a file

### Screenshot

_No response_

### Logs

```shell
-
```


### System Info

```shell
Latest main
```


### Severity

annoying

Hints from issue discussion:
As discussed via Slack DM, I'd like to give this a shot @pngwn

Commit message: formatting

Patch to review:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b011504189..9597243d11 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,7 +2,7 @@
 
 ## New Features:
 
-No changes to highlight.
+- The `gr.UploadButton` component now supports the `variant` and `interactive` parameters by [@abidlabs](https://github.com/abidlabs) in [PR 4436](https://github.com/gradio-app/gradio/pull/4436). 
 
 ## Bug Fixes:
 
diff --git a/gradio/components.py b/gradio/components.py
index b1afb2dad0..81dc45b197 100644
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -3337,7 +3337,7 @@ def __init__(
         self,
         value: str | Callable = "Run",
         *,
-        variant: str = "secondary",
+        variant: Literal["primary", "secondary", "stop"] = "secondary",
         visible: bool = True,
         interactive: bool = True,
         elem_id: str | None = None,
@@ -3378,7 +3378,7 @@ def get_config(self):
     @staticmethod
     def update(
         value: str | Literal[_Keywords.NO_VALUE] | None = _Keywords.NO_VALUE,
-        variant: str | None = None,
+        variant: Literal["primary", "secondary", "stop"] | None = None,
         visible: bool | None = None,
         interactive: bool | None = None,
     ):
@@ -3394,7 +3394,7 @@ def style(
         self,
         *,
         full_width: bool | None = None,
-        size: Literal["sm"] | Literal["lg"] | None = None,
+        size: Literal["sm", "lg"] | None = None,
         **kwargs,
     ):
         """
@@ -3427,7 +3427,9 @@ def __init__(
         label: str = "Upload a File",
         value: str | list[str] | Callable | None = None,
         *,
+        variant: Literal["primary", "secondary", "stop"] = "secondary",
         visible: bool = True,
+        interactive: bool = True,
         elem_id: str | None = None,
         elem_classes: list[str] | str | None = None,
         type: str = "file",
@@ -3437,12 +3439,14 @@ def __init__(
     ):
         """
         Parameters:
-            value: Default text for the button to display.
+            label: Text to display on the button. Defaults to "Upload a File".
+            value: File or list of files to upload by default.
+            variant: 'primary' for main call-to-action, 'secondary' for a more subdued style, 'stop' for a stop button.
             type: Type of value to be returned by component. "file" returns a temporary file object with the same base name as the uploaded file, whose full path can be retrieved by file_obj.name, "binary" returns an bytes object.
             file_count: if single, allows user to upload one file. If "multiple", user uploads multiple files. If "directory", user uploads all files in selected directory. Return type will be list for each file in case of "multiple" or "directory".
             file_types: List of type of files to be uploaded. "file" allows any file to be uploaded, "image" allows only image files to be uploaded, "audio" allows only audio files to be uploaded, "video" allows only video files to be uploaded, "text" allows only text files to be uploaded.
-            label: Text to display on the button. Defaults to "Upload a File".
             visible: If False, component will be hidden.
+            interactive: If False, the UploadButton will be in a disabled state.
             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.
         """
@@ -3458,6 +3462,7 @@ def __init__(
             )
         self.file_types = file_types
         self.label = label
+        self.variant = variant
         IOComponent.__init__(
             self,
             label=label,
@@ -3465,6 +3470,7 @@ def __init__(
             elem_id=elem_id,
             elem_classes=elem_classes,
             value=value,
+            interactive=interactive,
             **kwargs,
         )
 
@@ -3472,18 +3478,25 @@ def get_config(self):
         return {
             "label": self.label,
             "value": self.value,
+            "variant": self.variant,
             "file_count": self.file_count,
             "file_types": self.file_types,
+            "interactive": self.interactive,
             **Component.get_config(self),
         }
 
     @staticmethod
     def update(
-        value: str | Literal[_Keywords.NO_VALUE] | None = _Keywords.NO_VALUE,
+        value: str
+        | list[str]
+        | Literal[_Keywords.NO_VALUE]
+        | None = _Keywords.NO_VALUE,
+        variant: Literal["primary", "secondary", "stop"] | None = None,
         interactive: bool | None = None,
         visible: bool | None = None,
     ):
         return {
+            "variant": variant,
             "interactive": interactive,
             "visible": visible,
             "value": value,
@@ -3556,7 +3569,7 @@ def style(
         self,
         *,
         full_width: bool | None = None,
-        size: Literal["sm"] | Literal["lg"] | None = None,
+        size: Literal["sm", "lg"] | None = None,
         **kwargs,
     ):
         """
diff --git a/gradio/queueing.py b/gradio/queueing.py
index 525551b1a7..8182c8c76d 100644
--- a/gradio/queueing.py
+++ b/gradio/queueing.py
@@ -6,7 +6,7 @@
 import time
 from asyncio import TimeoutError as AsyncTimeOutError
 from collections import deque
-from typing import Any, Deque
+from typing import Any
 
 import fastapi
 import httpx
@@ -46,7 +46,7 @@ def __init__(
         max_size: int | None,
         blocks_dependencies: list,
     ):
-        self.event_queue: Deque[Event] = deque()
+        self.event_queue: deque[Event] = deque()
         self.events_pending_reconnection = []
         self.stopped = False
         self.max_thread_count = concurrency_count
diff --git a/js/app/src/components/UploadButton/UploadButton.svelte b/js/app/src/components/UploadButton/UploadButton.svelte
index b1aae9469d..bc5b70f44e 100644
--- a/js/app/src/components/UploadButton/UploadButton.svelte
+++ b/js/app/src/components/UploadButton/UploadButton.svelte
@@ -16,6 +16,9 @@
 	export let file_count: string;
 	export let file_types: Array<string> = ["file"];
 	export let root: string;
+	export let mode: "static" | "dynamic" = "dynamic";
+	export let variant: "primary" | "secondary" | "stop" = "secondary";
+	console.log("mode", mode);
 
 	async function handle_upload({ detail }: CustomEvent<FileData>) {
 		value = detail;
@@ -59,6 +62,8 @@
 	{visible}
 	{file_count}
 	{file_types}
+	{mode}
+	{variant}
 	on:click
 	on:load={handle_upload}
 >
diff --git a/js/app/src/components/UploadButton/index.ts b/js/app/src/components/UploadButton/index.ts
index 42ba498459..618464456f 100644
--- a/js/app/src/components/UploadButton/index.ts
+++ b/js/app/src/components/UploadButton/index.ts
@@ -1,2 +1,2 @@
 export { default as Component } from "./UploadButton.svelte";
-export const modes = ["static"];
+export const modes = ["static", "dynamic"];
diff --git a/js/upload-button/src/UploadButton.svelte b/js/upload-button/src/UploadButton.svelte
index 4cee54fcf8..b622096b22 100644
--- a/js/upload-button/src/UploadButton.svelte
+++ b/js/upload-button/src/UploadButton.svelte
@@ -12,6 +12,8 @@
 	export let file_count: string;
 	export let file_types: Array<string> = ["file"];
 	export let include_file_metadata = true;
+	export let mode: "static" | "dynamic" = "dynamic";
+	export let variant: "primary" | "secondary" | "stop" = "secondary";
 
 	let hidden_upload: HTMLInputElement;
 	const dispatch = createEventDispatcher();
@@ -83,12 +85,13 @@
 
 <Button
 	{size}
-	variant="secondary"
+	{variant}
 	{elem_id}
 	{elem_classes}
 	{visible}
 	on:click={openFileUpload}
 	{style}
+	disabled={mode === "static"}
 >
 	<slot />
 </Button>


Code review comments:
diff_hunk: @@ -16,6 +16,9 @@
 	export let file_count: string;
 	export let file_types: Array<string> = ["file"];
 	export let root: string;
+	export let mode: "static" | "dynamic" = "dynamic";
+	export let variant: "primary" | "secondary" | "stop" = "secondary";
+	console.log("mode", mode);
path: js/app/src/components/UploadButton/UploadButton.svelte
text: @user1:
remove console.log

@author:
Whoops fixed