501 lines
10 KiB
Markdown
501 lines
10 KiB
Markdown
<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>
|