funkwhale/front/ui-docs/components/ui/button.md

501 lines
10 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import Button from '~/components/ui/Button.vue'
import Layout from '~/components/ui/Layout.vue'
const click = ():Promise<void> => new Promise(resolve => setTimeout(resolve, 1000))
</script>
```ts
import Button from "~/components/ui/Button.vue"
```
# Button
Buttons are UI elements that users can interact with to perform actions and manipulate objects. They are distinct from [Links](link) and will not change the user's position.
```ts
{
thinFont?: true
lowHeight?: true
isActive?: boolean
isLoading?: boolean
shadow?: boolean
round?: boolean
icon?: string
onClick?: (...args: any[]) => void | Promise<void>
autofocus?: boolean
ariaPressed?: true
} & (ColorProps | DefaultProps)
& VariantProps
& RaisedProps
& WidthProps
& AlignmentProps
```
## Action
[The default action of buttons is `submit` \[mdn\]](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#type). Specify an [`onClick` or `@click`](#short-form-click) function to change the behavior of a button.
```vue-html
<form>
<Button>Without `onClick`</Button>
<Button :onClick="() => {}">With `onClick`</Button>
</form>
```
<form>
<Button>Without `onClick`</Button>
<Button :onClick="() => {}">With `onClick`</Button>
</form>
### Short-form @click
For convenience, you can use the Vue-specific `@click` syntax to add the `onClick` prop. The following two buttons are effectively equal:
```vue-html
<Button :onClick="() => { console.log('Hi!') }"> Long form </Button>
<Button @click="console.log('Hi!')"> Short form </Button>
```
<Button :onClick="() => { console.log('Hi!') }"> Long form </Button>
<Button @click="console.log('Hi!')"> Short form </Button>
## Button colors
Buttons come in different types depending on the type of action they represent.
Find [a complete overview of recommended styles on the color page](../../using-color#links-and-buttons).
### Default
Default buttons represent **neutral** actions such as cancelling a change or dismissing a notification.
<Layout grid class="preview transparent">
<div style="grid-column: span 3">
```vue-html
<Button>
Default button
</Button>
```
</div>
<Button>
Default button
</Button>
</Layout>
### Primary
The primary button represents the **single positive** action on a page or modal, such as uploading, confirming, and accepting changes.
<Layout grid class="preview">
<div style="grid-column: span 3">
```vue-html
<Button primary>
Primary button
</Button>
```
</div>
<Button primary>
Primary button
</Button>
</Layout>
### Secondary
Secondary buttons represent **neutral** actions such as cancelling a change or dismissing a notification.
<Layout grid class="preview secondary">
<div style="grid-column: span 4">
```vue-html
<Button secondary raised>
Secondary button
</Button>
```
</div>
<Button secondary raised>
Secondary button
</Button>
</Layout>
Note that on a secondary background, the button needs to be `raised` to make it stand out.
### Destructive
Desctrutive buttons represent **dangerous** actions including deleting items or purging domain information.
<Layout grid class="preview">
<div style="grid-column: span 3">
```vue-html
<Button destructive>
Destructive button
</Button>
```
</div>
<Button destructive>
Destructive button
</Button>
</Layout>
## Button variants
Buttons come in different styles that you can use depending on the location of the button.
### Solid
Solid buttons have a filled background. Use these to emphasize the action the button performs.
::: info
This is the default style. If you don't specify a style, a solid button is rendered.
:::
```vue-html
<Button>
Filled button
</Button>
<Button solid>
Also filled button
</Button>
```
<Button>
Filled button
</Button>
<Button solid>
Also filled button
</Button>
### Outline
Outline buttons have a transparent background. Use these to deemphasize the action the button performs.
```vue-html
<Button outline secondary>
Outline button
</Button>
```
<Button outline secondary>
Outline button
</Button>
### Ghost
Ghost buttons have a transparent background and border. Use these to deemphasize the action the button performs.
```vue-html
<Button ghost secondary>
Ghost button
</Button>
```
<Button ghost secondary>
Ghost button
</Button>
## Button styles
### Shadow
You can give a button a shadow to add depth.
```vue-html
<Button shadow>
Shadow button
</Button>
```
<Button shadow>
Shadow button
</Button>
## Button shapes
You can choose different shapes for buttons depending on their location and use.
### Normal
Normal buttons are slightly rounded rectangles.
::: info
This is the default shape. If you don't specify a type, a normal button is rendered.
:::
```vue-html
<Button>
Normal button
</Button>
```
<Button>
Normal button
</Button>
### Round
Round buttons have fully rounded edges.
```vue-html
<Button round>
Round button
</Button>
```
<Button round>
Round button
</Button>
## Split button
<Layout class="preview default raised">
<Button split splitIcon="bi-star" splitTitle="Star!" :onSplitClick="()=>console.log('1 star')">I am split</Button>
<Button> I am not split </Button>
<Button disabled> I am not split and I am disabled </Button>
<Button disabled split splitIcon="bi-star" splitTitle="Star!" :onSplitClick="()=>console.log('1 star')">I am split and disabled</Button>
</Layout>
## Button states
### On/Off
You can force an active state by passing an `aria-pressed` prop.
::: tip When do I use a Toggle vs. a Button?
**Use a Button with an `aria-pressed` prop**
- if the semantics of the option change depending whether it's on or off
- to perform asynchronous, stateful and fallible actions
**Examples:**
- Toggle a remote property
- Open/close a section in the UI
- Toolbar buttons that toggle through many options such as "Paragraph/Heading/List"
**Use the [Toggle component](toggle) (a.k.a. switch)**
- for options that don't cause side-effects and never change the Toggle label content based on the Toggle state (think of the traditional checkbox).
- for options that don't have any intermediary state besides "on" and "off"
**Examples:**
- A checkbox in the User settings
- A checkbox in a form that the user submits later
:::
```vue-html
<Button>
Off
</Button>
<Button aria-pressed>
On
</Button>
```
**Default:**
<Button>
Off
</Button>
<Button aria-pressed>
On
</Button>
---
**Secondary:**
<Button secondary>
Off
</Button>
<Button secondary aria-pressed>
On
</Button>
---
**Primary:**
<Button primary>
Off
</Button>
<Button primary aria-pressed>
On
</Button>
### Disabled
Disabled buttons are non-interactive and inherit a less bold color than the one provided.
::: tip When do I use `disabled`?
Use the `disabled` property for buttons that the user expects at a certain position, for example in a toolbar or in a row of action buttons.
If there is just one button in a form and its action is disabled, you may instead just remove it.
:::
```vue-html
<Button disabled>
Disabled button
</Button>
```
<Button disabled>
Disabled button
</Button>
### Loading
If a user can't interact with a button until something has finished loading, you can add a spinner by passing the `is-loading` prop.
```vue-html
<Button is-loading>
Loading button
</Button>
```
<Button is-loading>
Loading button
</Button>
### Promise handling in `@click`
When a function passed to `@click` returns a promise, the button automatically toggles a loading state on click. When the promise resolves or is rejected, the loading state turns off.
::: danger
There is no promise rejection mechanism implemented in the `<Button>` component. Make sure the `@click` handler never rejects.
:::
```vue
<script setup lang="ts">
const click = () => new Promise((resolve) => setTimeout(resolve, 1000));
</script>
<template>
<Button @click="click"> Click me </Button>
</template>
```
<Button @click="click">
Click me
</Button>
You can override the promise state by passing a false `is-loading` prop.
```vue-html
<Button :is-loading="false">
Click me
</Button>
```
<Button :is-loading="false">
Click me
</Button>
## Add an icon
You can use [Bootstrap Icons](https://icons.getbootstrap.com/) in your button component.
::: info
- Icon buttons shrink down to the icon size if you don't pass any content. If you want to keep the button at full width with just an icon, add `button-width` as a prop.
- When combining icons with other content, prefix the icon prop with `right ` to place it after the content.
- To make icons large, add ` large` to the icon prop.
:::
```vue-html
<Button icon="bi-three-dots-vertical" />
<Button round icon="bi-x large" />
<Button primary icon="bi-save" button-width/>
<Button destructive icon="bi-trash">
Delete
</Button>
<Button low-height icon="right bi-chevron-right">
Next
</Button>
```
<Layout flex>
<Button icon="bi-three-dots-vertical" />
<Button round secondary icon="bi-x large" />
<Button primary icon="bi-save" button-width/>
<Button destructive icon="bi-trash">
Delete
</Button>
<Button low-height icon="right bi-chevron-right">
Next
</Button>
</Layout>
## Set width and alignment
See [Using width](/using-width) and [Using alignment](/using-alignment)
<Layout flex>
```vue-html
<Button min-content>🐌</Button>
<Button tiny>🐌</Button>
<Button buttonWidth>🐌</Button>
<Button small>🐌</Button>
<Button auto>🐌</Button>
<hr />
<Button alignSelf="start">🐌</Button>
<Button alignSelf="center">🐌</Button>
<Button alignSelf="end">🐌</Button>
<hr />
<Button alignText="left">🐌</Button>
<Button alignText="center">🐌</Button>
<Button alignText="right">🐌</Button>
```
<Layout class="preview solid primary" stack no-gap>
<Button min-content>🐌</Button>
<Button tiny>🐌</Button>
<Button buttonWidth>🐌</Button>
<Button small>🐌</Button>
<Button auto>🐌</Button>
<hr />
<Button alignSelf="start">🐌</Button>
<Button alignSelf="center">🐌</Button>
<Button alignSelf="end">🐌</Button>
<hr />
<Button alignText="left">🐌</Button>
<Button alignText="center">🐌</Button>
<Button alignText="right">🐌</Button>
</Layout>
</Layout>