171 lines
3.3 KiB
Vue
171 lines
3.3 KiB
Vue
<script setup>
|
|
import { computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
status: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
variant: {
|
|
type: String,
|
|
default: 'default', // default, dot, pill
|
|
validator: (v) => ['default', 'dot', 'pill'].includes(v),
|
|
},
|
|
size: {
|
|
type: String,
|
|
default: 'md', // sm, md, lg
|
|
validator: (v) => ['sm', 'md', 'lg'].includes(v),
|
|
},
|
|
color: {
|
|
type: String,
|
|
default: null, // If null, uses status-based colors
|
|
validator: (v) =>
|
|
!v ||
|
|
[
|
|
'gray',
|
|
'red',
|
|
'yellow',
|
|
'green',
|
|
'blue',
|
|
'indigo',
|
|
'purple',
|
|
'pink',
|
|
'amber',
|
|
'emerald',
|
|
].includes(v),
|
|
},
|
|
});
|
|
|
|
// Status-based color mapping
|
|
const statusColors = {
|
|
active: 'green',
|
|
inactive: 'gray',
|
|
archived: 'gray',
|
|
pending: 'yellow',
|
|
completed: 'green',
|
|
failed: 'red',
|
|
success: 'green',
|
|
error: 'red',
|
|
warning: 'yellow',
|
|
info: 'blue',
|
|
draft: 'gray',
|
|
published: 'green',
|
|
};
|
|
|
|
const badgeColor = computed(() => {
|
|
if (props.color) return props.color;
|
|
const lowerStatus = props.status.toLowerCase();
|
|
return statusColors[lowerStatus] || 'gray';
|
|
});
|
|
|
|
const sizeClasses = {
|
|
sm: {
|
|
text: 'text-xs',
|
|
padding: 'px-2 py-0.5',
|
|
dot: 'w-1.5 h-1.5',
|
|
},
|
|
md: {
|
|
text: 'text-sm',
|
|
padding: 'px-2.5 py-1',
|
|
dot: 'w-2 h-2',
|
|
},
|
|
lg: {
|
|
text: 'text-base',
|
|
padding: 'px-3 py-1.5',
|
|
dot: 'w-2.5 h-2.5',
|
|
},
|
|
};
|
|
|
|
const colorClasses = {
|
|
gray: {
|
|
bg: 'bg-gray-100',
|
|
text: 'text-gray-800',
|
|
dot: 'bg-gray-500',
|
|
border: 'border-gray-300',
|
|
},
|
|
red: {
|
|
bg: 'bg-red-100',
|
|
text: 'text-red-800',
|
|
dot: 'bg-red-500',
|
|
border: 'border-red-300',
|
|
},
|
|
yellow: {
|
|
bg: 'bg-yellow-100',
|
|
text: 'text-yellow-800',
|
|
dot: 'bg-yellow-500',
|
|
border: 'border-yellow-300',
|
|
},
|
|
green: {
|
|
bg: 'bg-green-100',
|
|
text: 'text-green-800',
|
|
dot: 'bg-green-500',
|
|
border: 'border-green-300',
|
|
},
|
|
blue: {
|
|
bg: 'bg-blue-100',
|
|
text: 'text-blue-800',
|
|
dot: 'bg-blue-500',
|
|
border: 'border-blue-300',
|
|
},
|
|
indigo: {
|
|
bg: 'bg-indigo-100',
|
|
text: 'text-indigo-800',
|
|
dot: 'bg-indigo-500',
|
|
border: 'border-indigo-300',
|
|
},
|
|
purple: {
|
|
bg: 'bg-purple-100',
|
|
text: 'text-purple-800',
|
|
dot: 'bg-purple-500',
|
|
border: 'border-purple-300',
|
|
},
|
|
pink: {
|
|
bg: 'bg-pink-100',
|
|
text: 'text-pink-800',
|
|
dot: 'bg-pink-500',
|
|
border: 'border-pink-300',
|
|
},
|
|
amber: {
|
|
bg: 'bg-amber-100',
|
|
text: 'text-amber-800',
|
|
dot: 'bg-amber-500',
|
|
border: 'border-amber-300',
|
|
},
|
|
emerald: {
|
|
bg: 'bg-emerald-100',
|
|
text: 'text-emerald-800',
|
|
dot: 'bg-emerald-500',
|
|
border: 'border-emerald-300',
|
|
},
|
|
};
|
|
|
|
const colors = computed(() => colorClasses[badgeColor.value] || colorClasses.gray);
|
|
const sizes = computed(() => sizeClasses[props.size] || sizeClasses.md);
|
|
</script>
|
|
|
|
<template>
|
|
<span
|
|
:class="[
|
|
'inline-flex items-center font-medium',
|
|
sizes.text,
|
|
sizes.padding,
|
|
colors.bg,
|
|
colors.text,
|
|
variant === 'pill' ? 'rounded-full' : 'rounded-md',
|
|
variant === 'default' ? `border ${colors.border}` : '',
|
|
]"
|
|
>
|
|
<span
|
|
v-if="variant === 'dot'"
|
|
:class="[
|
|
'rounded-full mr-1.5',
|
|
sizes.dot,
|
|
colors.dot,
|
|
]"
|
|
></span>
|
|
<slot>{{ status }}</slot>
|
|
</span>
|
|
</template>
|
|
|
|
|