Skip to content

Commit 7db3017

Browse files
committed
feat(button): add loading state functionality and documentation for Button component
1 parent e038573 commit 7db3017

File tree

2 files changed

+108
-3
lines changed

2 files changed

+108
-3
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
title: Button with Loading States
3+
description: Guide for using the enhanced Button component with built-in loading states.
4+
sidebar: Button Loading
5+
---
6+
7+
# Button with Loading States
8+
9+
The Button component includes built-in loading state functionality for async operations.
10+
11+
## Component Location
12+
13+
```
14+
services/frontend/src/components/ui/button/
15+
├── Button.vue # Enhanced button component with loading states
16+
└── index.ts # Button variants and exports
17+
```
18+
19+
## Features
20+
21+
- **Automatic spinner** with `Loader2` icon from lucide-vue-next
22+
- **Auto-disable** during loading to prevent double submissions
23+
- **Optional loading text** to display custom messages
24+
- **Size-aware spinner** that scales with button size
25+
- **Works with all variants** (default, destructive, outline, etc.)
26+
27+
## Props
28+
29+
| Prop | Type | Default | Description |
30+
|------|------|---------|-------------|
31+
| `loading` | `boolean` | `false` | Shows spinner and disables button |
32+
| `loadingText` | `string` | `undefined` | Optional text during loading |
33+
| `disabled` | `boolean` | `false` | Disable independent of loading |
34+
| `variant` | `string` | `'default'` | Button style variant |
35+
| `size` | `string` | `'default'` | Button size (sm, default, lg, icon) |
36+
37+
## Usage Example
38+
39+
```vue
40+
<script setup lang="ts">
41+
import { ref } from 'vue'
42+
import { Button } from '@/components/ui/button'
43+
import { toast } from 'vue-sonner'
44+
45+
const isSubmitting = ref(false)
46+
47+
const handleSubmit = async () => {
48+
isSubmitting.value = true
49+
try {
50+
await saveData()
51+
toast.success('Saved successfully')
52+
} catch (error) {
53+
toast.error('Save failed', { description: error.message })
54+
} finally {
55+
isSubmitting.value = false
56+
}
57+
}
58+
</script>
59+
60+
<template>
61+
<Button
62+
:loading="isSubmitting"
63+
loading-text="Saving..."
64+
@click="handleSubmit"
65+
>
66+
Save Changes
67+
</Button>
68+
</template>
69+
```
70+
71+
## Implementation Details
72+
73+
The component automatically:
74+
- Displays a spinning `Loader2` icon when `loading` is true
75+
- Hides the original slot content during loading
76+
- Shows `loadingText` alongside the spinner (if provided)
77+
- Disables the button to prevent multiple clicks
78+
- Adjusts spinner size based on button size prop
79+
80+
For implementation details, see the source code at `services/frontend/src/components/ui/button/Button.vue`.
81+
82+
## Related Documentation
83+
84+
- [UI Design System](/development/frontend/ui-design-system) - Overall design patterns
85+
- [Global Sonner Toast System](/development/frontend/ui-design-global-sonner) - Toast notifications

docs/development/frontend/ui-design-system.mdx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,20 @@ Use `AlertDialog` for forms in modals:
172172

173173
## Button Patterns
174174

175+
### Loading States
176+
Buttons now include built-in loading state functionality. For comprehensive loading button documentation, see the [Button Loading States Guide](/development/frontend/ui-design-button-loading).
177+
178+
```html
179+
<!-- Button with loading state -->
180+
<Button
181+
:loading="isSubmitting"
182+
loading-text="Saving..."
183+
@click="handleSave"
184+
>
185+
Save Changes
186+
</Button>
187+
```
188+
175189
### Primary Actions
176190
```html
177191
<Button @click="handlePrimaryAction">
@@ -192,18 +206,24 @@ Use `AlertDialog` for forms in modals:
192206
<Button
193207
variant="destructive"
194208
@click="handleDelete"
209+
:loading="isDeleting"
210+
loading-text="Deleting..."
195211
class="bg-destructive text-destructive-foreground hover:bg-destructive/90"
196212
>
197-
<Trash2 class="h-4 w-4 mr-2" />
213+
<Trash2 v-if="!isDeleting" class="h-4 w-4 mr-2" />
198214
{{ t('actions.delete') }}
199215
</Button>
200216
```
201217

202218
### Icon-Only Buttons
203219
```html
204-
<Button variant="ghost" class="h-8 w-8 p-0">
220+
<Button
221+
variant="ghost"
222+
size="icon"
223+
:loading="isRefreshing"
224+
>
205225
<span class="sr-only">{{ t('actions.menu') }}</span>
206-
<MoreHorizontal class="h-4 w-4" />
226+
<MoreHorizontal v-if="!isRefreshing" class="h-4 w-4" />
207227
</Button>
208228
```
209229

0 commit comments

Comments
 (0)