Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | <template>
<div :class="spinnerClasses">
<svg
:class="[sizeClasses, 'text-primary-600']"
class="animate-spin"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
/>
</svg>
<span v-if="text" :class="textClasses">{{ text }}</span>
</div>
</template>
<script setup lang="ts">
/**
* BaseSpinner
* @description Loading spinner component with optional text
* Can be used inline or centered
*/
interface Props {
/** Size of the spinner */
size?: 'sm' | 'md' | 'lg'
/** Optional loading text */
text?: string
/** Center the spinner */
center?: boolean
}
const props = withDefaults(defineProps<Props>(), {
size: 'md',
center: false
})
const spinnerClasses = computed(() => [
'flex items-center gap-2',
props.center ? 'justify-center' : ''
])
const sizeClasses = computed(() => {
const sizes = {
sm: 'h-4 w-4',
md: 'h-8 w-8',
lg: 'h-12 w-12'
}
return sizes[props.size]
})
const textClasses = computed(() => {
const textSizes = {
sm: 'text-sm',
md: 'text-base',
lg: 'text-lg'
}
return [textSizes[props.size], 'text-secondary-600']
})
</script>
|