191 lines
4.4 KiB
Vue

<script setup>
import { ref, computed, watch } from 'vue';
import { Calendar } from 'v-calendar';
import 'v-calendar/style.css';
// Props del componente
const props = defineProps({
// Días de conflicto a pintar en rojo
conflictDays: {
type: Array,
default: () => []
},
// Configuración adicional
locale: {
type: String,
default: 'es'
}
});
// Emits
const emit = defineEmits(['monthChanged']);
// Variables reactivas
const currentMonth = ref(new Date());
const fromPage = ref(null);
// Atributos para el calendario (eventos, vacaciones, etc.)
const attributes = computed(() => {
const attrs = [];
// Agregar días de conflicto (pintarlos en rojo)
if (props.conflictDays && props.conflictDays.length > 0) {
props.conflictDays.forEach((day) => {
const currentYear = currentMonth.value.getFullYear();
const currentMonthNumber = currentMonth.value.getMonth();
const conflictDate = new Date(currentYear, currentMonthNumber, day);
attrs.push({
key: `conflict-${day}`,
dates: conflictDate,
highlight: {
color: 'red',
fillMode: 'solid'
},
popover: {
label: `Día con conflictos de vacaciones`,
visibility: 'hover'
}
});
});
}
return attrs;
});
// Configuración del calendario
const calendarConfig = computed(() => ({
locale: props.locale,
firstDayOfWeek: 2, // Lunes
masks: {
weekdays: 'WWW',
navMonths: 'MMMM',
title: 'MMMM YYYY'
},
theme: {
isDark: false
}
}));
// Métodos
const onPageChanged = (page) => {
// Actualizar el mes actual cuando se cambia de página
currentMonth.value = new Date(page[0].year, page[0].month - 1, 1);
emit('monthChanged', page[0].month);
};
// Watch para re-renderizar cuando cambien los días de conflicto o el mes actual
watch([() => props.conflictDays, currentMonth], () => {
// Los attributes se recalcularán automáticamente al cambiar estas dependencias
}, { deep: true });
// Exponer métodos para uso externo
defineExpose({
currentMonth
});
</script>
<template>
<div class="calendar-container bg-white rounded-lg shadow-md p-4">
<!-- Header del calendario -->
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-semibold text-gray-900">Calendario</h3>
</div>
<!-- Calendario principal -->
<div class="calendar-wrapper">
<Calendar
v-model="currentMonth"
:attributes="attributes"
:locale="calendarConfig.locale"
:first-day-of-week="calendarConfig.firstDayOfWeek"
:masks="calendarConfig.masks"
class="custom-calendar"
v-model:from-page="fromPage"
@update:from-page="onPageChanged"
@did-move="onPageChanged"
expanded
/>
</div>
</div>
</template>
<style scoped>
/* Estilos personalizados para el calendario */
.calendar-container {
min-width: 300px;
}
.custom-calendar {
width: 100%;
}
/* Personalización de v-calendar */
:deep(.vc-container) {
--vc-border-radius: 0.5rem;
--vc-weekday-color: #6B7280;
--vc-popover-content-bg: white;
--vc-popover-content-border: 1px solid #E5E7EB;
border: none;
font-family: inherit;
}
:deep(.vc-header) {
padding: 1rem 1rem 0.5rem;
}
:deep(.vc-title) {
font-size: 1rem;
font-weight: 600;
color: #1F2937;
}
:deep(.vc-weekday) {
color: #6B7280;
font-size: 0.75rem;
font-weight: 500;
padding: 0.5rem 0;
}
:deep(.vc-day) {
min-height: 2rem;
}
:deep(.vc-day-content) {
width: 2rem;
height: 2rem;
border-radius: 0.375rem;
border: none;
font-weight: 500;
transition: all 0.2s ease;
}
:deep(.vc-day-content:hover) {
background-color: #EFF6FF;
color: #2563EB;
}
:deep(.vc-day-content.vc-day-content-today) {
background-color: #DBEAFE;
color: #1D4ED8;
font-weight: 600;
}
:deep(.vc-highlights .vc-highlight) {
border-radius: 0.375rem;
}
:deep(.vc-dots) {
margin-bottom: 0.125rem;
}
/* Responsive */
@media (max-width: 640px) {
.calendar-container {
min-width: auto;
margin: 0 -1rem;
border-radius: 0;
}
}
</style>