forked from BRETT/BRETT
Shared Spinner component for button loading states
Adds a reusable shadcn-style Spinner (lucide loader-circle icon, localized aria-label) and wires it into every button that has a pending state: comment submit, delete, tag save, profile save, admin saves, upload, login and register. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
PopoverTitle,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
|
||||
const { user } = useAuth()
|
||||
const { t } = useI18n()
|
||||
@@ -82,6 +83,7 @@ async function onSubmit() {
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<Button type="submit" class="w-full" :disabled="loading || (!file && !url.trim())">
|
||||
<Spinner v-if="loading" />
|
||||
{{ loading ? $t('upload.uploading') : $t('upload.upload') }}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class']
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Icon
|
||||
name="lucide:loader-circle"
|
||||
role="status"
|
||||
:aria-label="$t('common.loading')"
|
||||
:class="cn('size-4 animate-spin', props.class)"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Spinner } from './Spinner.vue'
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
|
||||
definePageMeta({ middleware: 'admin' })
|
||||
@@ -219,6 +220,7 @@ async function saveMotd() {
|
||||
:disabled="savingRole === role.id"
|
||||
@click="savePermissions(role)"
|
||||
>
|
||||
<Spinner v-if="savingRole === role.id" />
|
||||
{{ savingRole === role.id ? $t('common.saving') : $t('common.save') }}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -323,6 +325,7 @@ async function saveMotd() {
|
||||
class="min-h-16"
|
||||
/>
|
||||
<Button type="submit" class="shrink-0" :disabled="motdSaving">
|
||||
<Spinner v-if="motdSaving" />
|
||||
{{ motdSaving ? $t('common.saving') : $t('common.save') }}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
|
||||
const { login } = useAuth()
|
||||
const { t } = useI18n()
|
||||
@@ -50,6 +51,7 @@ async function onSubmit() {
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<Button type="submit" class="w-full" :disabled="loading">
|
||||
<Spinner v-if="loading" />
|
||||
{{ $t('auth.login') }}
|
||||
</Button>
|
||||
<p class="text-center text-sm text-muted-foreground">
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
|
||||
const { register } = useAuth()
|
||||
const { t } = useI18n()
|
||||
@@ -55,6 +56,7 @@ async function onSubmit() {
|
||||
{{ errorMessage }}
|
||||
</p>
|
||||
<Button type="submit" class="w-full" :disabled="loading">
|
||||
<Spinner v-if="loading" />
|
||||
{{ $t('auth.createAccount') }}
|
||||
</Button>
|
||||
<p class="text-center text-sm text-muted-foreground">
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
|
||||
const { user, refresh } = useAuth()
|
||||
const { invalidate } = useUserSummaries()
|
||||
@@ -268,6 +269,7 @@ async function save() {
|
||||
<FileDropzone v-model="avatar" accept="image/*" :hint="t('upload.avatarHint')" />
|
||||
</div>
|
||||
<Button type="submit" class="w-full" :disabled="saving">
|
||||
<Spinner v-if="saving" />
|
||||
{{ saving ? $t('common.saving') : $t('common.save') }}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
Generated
+10
@@ -10,6 +10,7 @@
|
||||
"@dnd-kit/abstract": "^0.1.21",
|
||||
"@iconify-json/ph": "^1.2.2",
|
||||
"@iconify-json/simple-icons": "^1.2.98",
|
||||
"@lucide/vue": "^1.51.0",
|
||||
"@nuxt/image": "^2.1.0",
|
||||
"@nuxtjs/i18n": "^10.6.0",
|
||||
"@tanstack/vue-table": "^9.2.4",
|
||||
@@ -3575,6 +3576,15 @@
|
||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@lucide/vue": {
|
||||
"version": "1.51.0",
|
||||
"resolved": "https://registry.npmjs.org/@lucide/vue/-/vue-1.51.0.tgz",
|
||||
"integrity": "sha512-zMkTAMP8OsPESz/GDedsZfr3VUF2IiHlj25mBZG6Pc8A8J8BdygkE8k1HhRFXEUNzytp5DqxiBQNm3RG/U2bVg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"vue": ">=3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"@dnd-kit/abstract": "^0.1.21",
|
||||
"@iconify-json/ph": "^1.2.2",
|
||||
"@iconify-json/simple-icons": "^1.2.98",
|
||||
"@lucide/vue": "^1.51.0",
|
||||
"@nuxt/image": "^2.1.0",
|
||||
"@nuxtjs/i18n": "^10.6.0",
|
||||
"@tanstack/vue-table": "^9.2.4",
|
||||
|
||||
Reference in New Issue
Block a user