138 lines
4.7 KiB
Vue
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> |