138 lines
4.7 KiB
Vue

<template>
<button :type="type" :disabled="disabled || loading" :class="buttonClasses" @click="handleClick">
<svg v-if="loading" class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z">
</path>
</svg>
<slot></slot>
</button>
</template>
<script setup lang="ts">
import { computed } from 'vue';
interface Props {
variant?: 'solid' | 'outline' | 'ghost' | 'smooth';
color?: 'primary' | 'danger' | 'success' | 'info' | 'warning';
size?: 'sm' | 'md' | 'lg';
type?: 'button' | 'submit' | 'reset';
disabled?: boolean;
loading?: boolean;
fullWidth?: boolean;
iconOnly?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
variant: 'solid',
color: 'primary',
size: 'md',
type: 'button',
disabled: false,
loading: false,
fullWidth: false,
iconOnly: false,
});
const emit = defineEmits<{
click: [event: MouseEvent];
}>();
function handleClick(event: MouseEvent) {
if (props.disabled || props.loading) return;
emit('click', event);
}
const buttonClasses = computed(() => {
const baseClasses = [
'inline-flex',
'items-center',
'justify-center',
'font-medium',
'rounded-lg',
'transition-all',
'duration-200',
'focus:outline-none',
'focus:ring-2',
'focus:ring-offset-2',
'disabled:opacity-50',
'disabled:cursor-not-allowed',
];
const sizeClasses = {
sm: ['px-3', 'py-1.5', 'text-sm'],
md: ['px-4', 'py-2', 'text-sm'],
lg: ['px-6', 'py-3', 'text-base'],
};
// Estilos base por tipo
const variantClasses = {
solid: ['shadow-sm'],
outline: ['border', 'bg-white', 'hover:bg-gray-50'],
ghost: ['bg-transparent', 'hover:bg-gray-100'],
smooth: ['bg-opacity-20', 'font-bold', 'uppercase', 'shadow-none'],
};
// Colores por tipo
const colorClasses = {
primary: {
solid: ['bg-primary-600', 'text-white', 'hover:bg-primary-700', 'focus:ring-primary-500'],
outline: ['border-primary-600', 'text-primary-600', 'focus:ring-primary-500'],
ghost: ['text-primary-600', 'focus:ring-primary-500'],
smooth: ['bg-primary-100', 'text-primary-600', 'hover:bg-primary-200', 'focus:ring-primary-300'],
},
danger: {
solid: ['bg-red-600', 'text-white', 'hover:bg-red-700', 'focus:ring-red-500'],
outline: ['border-red-600', 'text-red-600', 'focus:ring-red-500'],
ghost: ['text-red-600', 'focus:ring-red-500'],
smooth: ['bg-red-100', 'text-red-600', 'hover:bg-red-200', 'focus:ring-red-300'],
},
success: {
solid: ['bg-green-600', 'text-white', 'hover:bg-green-700', 'focus:ring-green-500'],
outline: ['border-green-600', 'text-green-600', 'focus:ring-green-500'],
ghost: ['text-green-600', 'focus:ring-green-500'],
smooth: ['bg-green-100', 'text-green-600', 'hover:bg-green-200', 'focus:ring-green-300'],
},
info: {
solid: ['bg-blue-600', 'text-white', 'hover:bg-blue-700', 'focus:ring-blue-500'],
outline: ['border-blue-600', 'text-blue-600', 'focus:ring-blue-500'],
ghost: ['text-blue-600', 'focus:ring-blue-500'],
smooth: ['bg-blue-100', 'text-blue-600', 'hover:bg-blue-200', 'focus:ring-blue-300'],
},
warning: {
solid: ['bg-yellow-500', 'text-white', 'hover:bg-yellow-600', 'focus:ring-yellow-400'],
outline: ['border-yellow-500', 'text-yellow-600', 'focus:ring-yellow-400'],
ghost: ['text-yellow-600', 'focus:ring-yellow-400'],
smooth: ['bg-yellow-100', 'text-yellow-600', 'hover:bg-yellow-200', 'focus:ring-yellow-200'],
},
};
// Asegura que siempre haya un array válido para los estilos
const safeVariant = props.variant && variantClasses[props.variant] ? props.variant : 'solid';
const safeColor = props.color && colorClasses[props.color] ? props.color : 'primary';
const classes = [
...baseClasses,
...sizeClasses[props.size],
...(variantClasses[safeVariant] || []),
...(colorClasses[safeColor]?.[safeVariant] || []),
];
if (props.fullWidth) {
classes.push('w-full');
}
if (props.iconOnly) {
if (props.size === 'sm') classes.push('w-8', 'h-8', 'p-0');
else if (props.size === 'lg') classes.push('w-12', 'h-12', 'p-0');
else classes.push('w-10', 'h-10', 'p-0');
} else if (props.fullWidth) {
classes.push('w-full');
}
return classes;
});
</script>