133 lines
2.7 KiB
Vue
133 lines
2.7 KiB
Vue
<script setup>
|
|
defineOptions({ name: "Progress" });
|
|
|
|
const props = defineProps({
|
|
value: {
|
|
type: Number,
|
|
required: true,
|
|
validator: (value) => value >= 0 && value <= 100,
|
|
},
|
|
size: {
|
|
type: String,
|
|
default: "default",
|
|
validator: (value) => ["sm", "default", "lg"].includes(value),
|
|
},
|
|
showValue: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
variant: {
|
|
type: String,
|
|
default: "default",
|
|
validator: (value) =>
|
|
["default", "success", "info", "warning", "danger"].includes(value),
|
|
},
|
|
animated: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
striped: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
const sizeClasses = {
|
|
sm: "!h-2",
|
|
default: "!h-4",
|
|
lg: "!h-6",
|
|
};
|
|
|
|
const variantClasses = {
|
|
default: "bg-primary",
|
|
success: "bg-[rgb(var(--success))]",
|
|
info: "bg-[rgb(var(--info))]",
|
|
warning: "bg-[rgb(var(--warning))]",
|
|
danger: "bg-[rgb(var(--danger))]",
|
|
};
|
|
|
|
const textVariantClasses = {
|
|
default: "!text-primary-foreground",
|
|
success: "!text-[rgb(var(--success-foreground))]",
|
|
info: "!text-[rgb(var(--info-foreground))]",
|
|
warning: "!text-[rgb(var(--warning-foreground))]",
|
|
danger: "!text-[rgb(var(--danger-foreground))]",
|
|
};
|
|
|
|
const progressClasses = computed(() => {
|
|
return [
|
|
sizeClasses[props.size],
|
|
variantClasses[props.variant],
|
|
{
|
|
"animate-progress": props.animated,
|
|
"bg-striped": props.striped,
|
|
},
|
|
];
|
|
});
|
|
|
|
const valueStyles = computed(() => ({
|
|
width: `${props.value}%`,
|
|
transition: props.animated ? "width 0.3s ease-in-out" : "none",
|
|
}));
|
|
</script>
|
|
|
|
<template>
|
|
<div class="relative w-full">
|
|
<div
|
|
class="relative h-full w-full overflow-hidden rounded-full bg-secondary"
|
|
role="progressbar"
|
|
:aria-valuenow="value"
|
|
aria-valuemin="0"
|
|
aria-valuemax="100"
|
|
>
|
|
<div
|
|
:class="progressClasses"
|
|
class="h-full w-full flex-1 rounded-full transition-all"
|
|
:style="valueStyles"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="showValue"
|
|
class="absolute inset-0 flex items-center justify-center text-xs font-medium"
|
|
:class="textVariantClasses[variant]"
|
|
>
|
|
{{ value }}%
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.bg-striped {
|
|
background-image: linear-gradient(
|
|
45deg,
|
|
rgba(255, 255, 255, 0.15) 25%,
|
|
transparent 25%,
|
|
transparent 50%,
|
|
rgba(255, 255, 255, 0.15) 50%,
|
|
rgba(255, 255, 255, 0.15) 75%,
|
|
transparent 75%,
|
|
transparent
|
|
);
|
|
background-size: 1rem 1rem;
|
|
}
|
|
|
|
.animate-progress {
|
|
animation: progress-animation 1s linear infinite;
|
|
}
|
|
|
|
@keyframes progress-animation {
|
|
from {
|
|
background-position: 1rem 0;
|
|
}
|
|
to {
|
|
background-position: 0 0;
|
|
}
|
|
}
|
|
|
|
/* Ensure text is visible on all backgrounds */
|
|
.text-xs {
|
|
text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
|
|
color: white;
|
|
}
|
|
</style>
|