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,
|
PopoverTitle,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui/popover'
|
} from '@/components/ui/popover'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
const { user } = useAuth()
|
const { user } = useAuth()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -82,6 +83,7 @@ async function onSubmit() {
|
|||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</p>
|
</p>
|
||||||
<Button type="submit" class="w-full" :disabled="loading || (!file && !url.trim())">
|
<Button type="submit" class="w-full" :disabled="loading || (!file && !url.trim())">
|
||||||
|
<Spinner v-if="loading" />
|
||||||
{{ loading ? $t('upload.uploading') : $t('upload.upload') }}
|
{{ loading ? $t('upload.uploading') : $t('upload.upload') }}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</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,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
import { Textarea } from '@/components/ui/textarea'
|
import { Textarea } from '@/components/ui/textarea'
|
||||||
|
|
||||||
definePageMeta({ middleware: 'admin' })
|
definePageMeta({ middleware: 'admin' })
|
||||||
@@ -219,6 +220,7 @@ async function saveMotd() {
|
|||||||
:disabled="savingRole === role.id"
|
:disabled="savingRole === role.id"
|
||||||
@click="savePermissions(role)"
|
@click="savePermissions(role)"
|
||||||
>
|
>
|
||||||
|
<Spinner v-if="savingRole === role.id" />
|
||||||
{{ savingRole === role.id ? $t('common.saving') : $t('common.save') }}
|
{{ savingRole === role.id ? $t('common.saving') : $t('common.save') }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -323,6 +325,7 @@ async function saveMotd() {
|
|||||||
class="min-h-16"
|
class="min-h-16"
|
||||||
/>
|
/>
|
||||||
<Button type="submit" class="shrink-0" :disabled="motdSaving">
|
<Button type="submit" class="shrink-0" :disabled="motdSaving">
|
||||||
|
<Spinner v-if="motdSaving" />
|
||||||
{{ motdSaving ? $t('common.saving') : $t('common.save') }}
|
{{ motdSaving ? $t('common.saving') : $t('common.save') }}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
const { login } = useAuth()
|
const { login } = useAuth()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -50,6 +51,7 @@ async function onSubmit() {
|
|||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</p>
|
</p>
|
||||||
<Button type="submit" class="w-full" :disabled="loading">
|
<Button type="submit" class="w-full" :disabled="loading">
|
||||||
|
<Spinner v-if="loading" />
|
||||||
{{ $t('auth.login') }}
|
{{ $t('auth.login') }}
|
||||||
</Button>
|
</Button>
|
||||||
<p class="text-center text-sm text-muted-foreground">
|
<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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
const { register } = useAuth()
|
const { register } = useAuth()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -55,6 +56,7 @@ async function onSubmit() {
|
|||||||
{{ errorMessage }}
|
{{ errorMessage }}
|
||||||
</p>
|
</p>
|
||||||
<Button type="submit" class="w-full" :disabled="loading">
|
<Button type="submit" class="w-full" :disabled="loading">
|
||||||
|
<Spinner v-if="loading" />
|
||||||
{{ $t('auth.createAccount') }}
|
{{ $t('auth.createAccount') }}
|
||||||
</Button>
|
</Button>
|
||||||
<p class="text-center text-sm text-muted-foreground">
|
<p class="text-center text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select'
|
} from '@/components/ui/select'
|
||||||
|
import { Spinner } from '@/components/ui/spinner'
|
||||||
|
|
||||||
const { user, refresh } = useAuth()
|
const { user, refresh } = useAuth()
|
||||||
const { invalidate } = useUserSummaries()
|
const { invalidate } = useUserSummaries()
|
||||||
@@ -268,6 +269,7 @@ async function save() {
|
|||||||
<FileDropzone v-model="avatar" accept="image/*" :hint="t('upload.avatarHint')" />
|
<FileDropzone v-model="avatar" accept="image/*" :hint="t('upload.avatarHint')" />
|
||||||
</div>
|
</div>
|
||||||
<Button type="submit" class="w-full" :disabled="saving">
|
<Button type="submit" class="w-full" :disabled="saving">
|
||||||
|
<Spinner v-if="saving" />
|
||||||
{{ saving ? $t('common.saving') : $t('common.save') }}
|
{{ saving ? $t('common.saving') : $t('common.save') }}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Generated
+10
@@ -10,6 +10,7 @@
|
|||||||
"@dnd-kit/abstract": "^0.1.21",
|
"@dnd-kit/abstract": "^0.1.21",
|
||||||
"@iconify-json/ph": "^1.2.2",
|
"@iconify-json/ph": "^1.2.2",
|
||||||
"@iconify-json/simple-icons": "^1.2.98",
|
"@iconify-json/simple-icons": "^1.2.98",
|
||||||
|
"@lucide/vue": "^1.51.0",
|
||||||
"@nuxt/image": "^2.1.0",
|
"@nuxt/image": "^2.1.0",
|
||||||
"@nuxtjs/i18n": "^10.6.0",
|
"@nuxtjs/i18n": "^10.6.0",
|
||||||
"@tanstack/vue-table": "^9.2.4",
|
"@tanstack/vue-table": "^9.2.4",
|
||||||
@@ -3575,6 +3576,15 @@
|
|||||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
|
"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",
|
"@dnd-kit/abstract": "^0.1.21",
|
||||||
"@iconify-json/ph": "^1.2.2",
|
"@iconify-json/ph": "^1.2.2",
|
||||||
"@iconify-json/simple-icons": "^1.2.98",
|
"@iconify-json/simple-icons": "^1.2.98",
|
||||||
|
"@lucide/vue": "^1.51.0",
|
||||||
"@nuxt/image": "^2.1.0",
|
"@nuxt/image": "^2.1.0",
|
||||||
"@nuxtjs/i18n": "^10.6.0",
|
"@nuxtjs/i18n": "^10.6.0",
|
||||||
"@tanstack/vue-table": "^9.2.4",
|
"@tanstack/vue-table": "^9.2.4",
|
||||||
|
|||||||
Reference in New Issue
Block a user