Skip to content

Commit

Permalink
Add icon and link params to gr.Button (#5080)
Browse files Browse the repository at this point in the history
* add icons and link params

* tweak

* add changeset

* prepend icon param with /file=

* prepend icon with /file=

* restore and add button stories

* add changeset

* param description tweak

* tweak param description

* type tweak

* fix i18n in story

* fix formatting

* fix test

* tweak

* fix typo

* fix icon bug

* fix inconsistent width

* remove <a /> target

* styling changes + add story

---------

Co-authored-by: gradio-pr-bot <[email protected]>
  • Loading branch information
hannahblair and gradio-pr-bot authored Aug 4, 2023
1 parent 3b9494f commit 37caa2e
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 18 deletions.
6 changes: 6 additions & 0 deletions .changeset/witty-suns-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@gradio/button": minor
"gradio": minor
---

feat:Add icon and link params to `gr.Button`
14 changes: 14 additions & 0 deletions gradio/components/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ def __init__(
*,
variant: Literal["primary", "secondary", "stop"] = "secondary",
size: Literal["sm", "lg"] | None = None,
icon: str | None = None,
link: str | None = None,
visible: bool = True,
interactive: bool = True,
elem_id: str | None = None,
Expand All @@ -43,6 +45,8 @@ def __init__(
value: Default text for the button to display. If callable, the function will be called whenever the app loads to set the initial value of the component.
variant: 'primary' for main call-to-action, 'secondary' for a more subdued style, 'stop' for a stop button.
size: Size of the button. Can be "sm" or "lg".
icon: URL or path to the icon file to display within the button. If None, no icon will be displayed.
link: URL to open when the button is clicked. If None, no link will be used.
visible: If False, component will be hidden.
interactive: If False, the Button 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.
Expand All @@ -67,11 +71,17 @@ def __init__(
self.variant = variant
self.size = size

self.icon = icon if icon is None else "/file=" + icon

self.link = link

def get_config(self):
return {
"value": self.value,
"variant": self.variant,
"size": self.size,
"icon": self.icon,
"link": self.link,
"interactive": self.interactive,
"scale": self.scale,
"min_width": self.min_width,
Expand All @@ -83,6 +93,8 @@ def update(
value: str | Literal[_Keywords.NO_VALUE] | None = _Keywords.NO_VALUE,
variant: Literal["primary", "secondary", "stop"] | None = None,
size: Literal["sm", "lg"] | None = None,
icon: str | None = None,
link: str | None = None,
visible: bool | None = None,
interactive: bool | None = None,
scale: int | None = None,
Expand All @@ -93,6 +105,8 @@ def update(
"size": size,
"visible": visible,
"value": value,
"icon": icon,
"link": link,
"interactive": interactive,
"scale": scale,
"min_width": min_width,
Expand Down
4 changes: 4 additions & 0 deletions gradio/components/clear_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def __init__(
value: str = "Clear",
variant: Literal["primary", "secondary", "stop"] = "secondary",
size: Literal["sm", "lg"] | None = None,
icon: str | None = None,
link: str | None = None,
visible: bool = True,
interactive: bool = True,
elem_id: str | None = None,
Expand All @@ -41,6 +43,8 @@ def __init__(
value,
variant=variant,
size=size,
icon=icon,
link=link,
visible=visible,
interactive=interactive,
elem_id=elem_id,
Expand Down
93 changes: 93 additions & 0 deletions js/button/Button.stories.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<script>
import { Meta, Template, Story } from "@storybook/addon-svelte-csf";
import Button from "./static/";
import { setupi18n } from "../app/src/i18n";
setupi18n();
</script>

<Meta
title="Components/Button"
component={Button}
argTypes={{
label: {
control: "text",
description: "The text to display on the button",
name: "label",
value: "Gradio Button",
},
variant: {
options: ["primary", "secondary", "stop"],
description: "The variant of the button",
control: { type: "select" },
defaultValue: "primary",
},
size: {
options: ["sm", "lg"],
description: "The size of the button",
control: { type: "select" },
defaultValue: "lg",
},
visible: {
options: [true, false],
description: "Sets the visibility of the button",
control: { type: "boolean" },
defaultValue: true,
},
interactive: {
options: [true, false],
description: "If false, the button will be in a disabled state",
control: { type: "boolean" },
defaultValue: true,
},
disabled: {
options: [true, false],
control: { type: "boolean" },
defaultValue: false,
},
scale: {
options: [null, 0.5, 1, 2],
description:
"relative width compared to adjacent Components in a Row. For example, if Component A has scale=2, and Component B has scale=1, A will be twice as wide as B. Should be an integer.",
control: { type: "select" },
},
}}
/>

<Template let:args>
<Button value="Gradio Button" {...args} />
</Template>

<Story name="Primary" args={{ variant: "primary", size: "lg", scale: 1 }} />
<Story name="Secondary" args={{ variant: "secondary", size: "lg" }} />
<Story name="Stop" args={{ variant: "stop", size: "lg" }} />
<Story
name="Button with link"
args={{ link: "https://huggingface.co/welcome" }}
/>
<Story
name="Button with external image icon"
args={{
icon: "https://huggingface.co/front/assets/huggingface_logo-noborder.svg",
}}
/>
<Story
name="Button with local icon file"
args={{
icon: "./HF_logo.svg",
}}
/>

<Story
name="Button with visible equal to false"
args={{
visible: false,
}}
/>

<Story
name="Button with local icon file"
args={{
icon: "./HF_logo.svg",
}}
/>
77 changes: 59 additions & 18 deletions js/button/static/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,58 @@
export let visible = true;
export let variant: "primary" | "secondary" | "stop" = "secondary";
export let size: "sm" | "lg" = "lg";
export let value: string | null = null;
export let link: string | null = null;
export let icon: string | null = null;
export let disabled = false;
export let scale: number | null = null;
export let min_width: number | undefined = undefined;
</script>

<button
on:click
class:hidden={!visible}
class="{size} {variant} {elem_classes.join(' ')}"
style:flex-grow={scale}
style:width={scale === 0 ? "fit-content" : null}
style:min-width={typeof min_width === "number"
? `calc(min(${min_width}px, 100%))`
: null}
id={elem_id}
{disabled}
>
<slot />
</button>
{#if link && link.length > 0}
<a
href={link}
rel="noopener noreferrer"
class:hidden={!visible}
class:disabled
aria-disabled={disabled}
class="{size} {variant} {elem_classes.join(' ')}"
style:flex-grow={scale}
style:pointer-events={disabled ? "none" : null}
style:width={scale === 0 ? "fit-content" : null}
style:min-width={typeof min_width === "number"
? `calc(min(${min_width}px, 100%))`
: null}
id={elem_id}
>
{#if icon}
<img class="button-icon" src={icon} alt={`${value}-icon`} />
{/if}
<slot />
</a>
{:else}
<button
on:click
class:hidden={!visible}
class="{size} {variant} {elem_classes.join(' ')}"
style:flex-grow={scale}
style:width={scale === 0 ? "fit-content" : null}
style:min-width={typeof min_width === "number"
? `calc(min(${min_width}px, 100%))`
: null}
id={elem_id}
{disabled}
>
{#if icon}
<img class="button-icon" src={icon} alt={`${value}-icon`} />
{/if}
<slot />
</button>
{/if}

<style>
button {
button,
a {
display: inline-flex;
justify-content: center;
align-items: center;
Expand All @@ -36,14 +66,19 @@
}
button:hover,
button[disabled] {
button[disabled],
a:hover,
a.disabled {
box-shadow: var(--button-shadow-hover);
}
button:active {
button:active,
a:active {
box-shadow: var(--button-shadow-active);
}
button[disabled] {
button[disabled],
a.disabled {
opacity: 0.5;
filter: grayscale(30%);
cursor: not-allowed;
Expand Down Expand Up @@ -105,4 +140,10 @@
font-weight: var(--button-large-text-weight);
font-size: var(--button-large-text-size);
}
.button-icon {
width: var(--text-xl);
height: var(--text-xl);
margin-right: var(--spacing-xl);
}
</style>
5 changes: 5 additions & 0 deletions js/button/static/StaticButton.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@
export let mode: "static" | "dynamic" = "dynamic";
export let size: "sm" | "lg" = "lg";
export let scale: number | null = null;
export let icon: string | null = null;
export let link: string | null = null;
export let min_width: number | undefined = undefined;
</script>

<Button
{value}
{variant}
{elem_id}
{elem_classes}
{size}
{scale}
{link}
{icon}
{min_width}
{visible}
disabled={mode === "static"}
Expand Down
73 changes: 73 additions & 0 deletions js/storybook/public/HF_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

1 comment on commit 37caa2e

@vercel
Copy link

@vercel vercel bot commented on 37caa2e Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.