Files
sub2api/frontend/src/components/common/LoadingSpinner.vue
2025-12-18 13:50:39 +08:00

66 lines
1.3 KiB
Vue

<template>
<div
:class="['spinner', sizeClasses, colorClass]"
role="status"
:aria-label="t('common.loading')"
>
<span class="sr-only">{{ t('common.loading') }}</span>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl'
type SpinnerColor = 'primary' | 'secondary' | 'white' | 'gray'
interface Props {
size?: SpinnerSize
color?: SpinnerColor
}
const props = withDefaults(defineProps<Props>(), {
size: 'md',
color: 'primary'
})
const sizeClasses = computed(() => {
const sizes: Record<SpinnerSize, string> = {
sm: 'w-4 h-4 border-2',
md: 'w-8 h-8 border-2',
lg: 'w-12 h-12 border-[3px]',
xl: 'w-16 h-16 border-4'
}
return sizes[props.size]
})
const colorClass = computed(() => {
const colors: Record<SpinnerColor, string> = {
primary: 'text-primary-500',
secondary: 'text-gray-500 dark:text-dark-400',
white: 'text-white',
gray: 'text-gray-400 dark:text-dark-500'
}
return colors[props.color]
})
</script>
<style scoped>
.spinner {
@apply inline-block rounded-full border-solid border-current border-r-transparent;
animation: spin 0.75s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>