Files
Timetable-V2/src/components/day-carousel.vue
minie4 dac0d09167 🐛 Fix issues with UTC time handling
- Using `.setUTCHours` can cause the day to shift at specific times
2023-06-11 23:54:18 +02:00

132 lines
3.7 KiB
Vue

<script setup>
import { selectedDate, changeDate, changeDay } from "@/store";
import { Swiper, SwiperSlide } from "swiper/vue";
import { Virtual } from "swiper";
import { ref, watch } from "vue";
import { getDateSkippingWeekend, setUTCMidnight } from "@/util";
import ScrollableContainer from "@/components/scrollable-container.vue";
defineProps({
element: {
required: true,
},
});
// Get swiper element
const swiperModules = ref([Virtual]);
const swiperElement = ref();
function setSwiper(swiper) {
swiperElement.value = swiper;
}
// Load current, previous and next day
const loadedDates = ref([
getDateSkippingWeekend(new Date(selectedDate.value.getTime() - 86400000)),
new Date(selectedDate.value.getTime()),
getDateSkippingWeekend(new Date(selectedDate.value.getTime() + 86400000)),
]);
// Load left or right date on slide change
function slideChange(swiper) {
const activeSlide = swiper.activeIndex;
// Only trigger data refresh if date is different
const newSelectedDate = loadedDates.value[activeSlide];
if (selectedDate.value.getTime() != newSelectedDate.getTime())
selectedDate.value = newSelectedDate;
// Check if current slide is the last one
if (activeSlide == loadedDates.value.length - 1) {
// Append next day to loadedDates
const lastDate = loadedDates.value.at(-1);
loadedDates.value.push(getDateSkippingWeekend(calculateDate(lastDate, 1)));
}
}
function slideChangeEnd(swiper) {
const activeSlide = swiper.activeIndex;
// Check if the current slide is the first one
if (activeSlide == 0) {
// Prepend the previous day to loadedDates
// and slide to the next slide without transition
// so the same day as before is shown
const lastDate = loadedDates.value[0];
loadedDates.value = [
getDateSkippingWeekend(calculateDate(lastDate, -1)),
...loadedDates.value,
];
swiper.slideTo(activeSlide + 1, 0);
}
}
// Also check if a day needs to be prepended
// if the user releases the card later because
// a different event is fired for that
function slideResetEnd(swiper) {
slideChangeEnd(swiper);
}
// Helper functions
function calculateDate(date, offset) {
return new Date(date.getTime() + 86400000 * offset);
}
function calculateSlide(date) {
return loadedDates.value.findIndex((e) => e.getTime() == date.getTime());
}
function slideToDate(date) {
const slide = calculateSlide(date);
if (slide != -1) swiperElement.value.slideTo(slide);
else {
// If the slide is not loaded yet reset
// loaded dates and load that date
loadedDates.value = [calculateDate(date, -1), date, calculateDate(date, 1)];
swiperElement.value.slideTo(1, 0);
}
selectedDate.value = date;
}
// Watch for slide change instructions
watch(changeDay, (change) => {
if (change == 0) return;
const calculatedDate = calculateDate(selectedDate.value, change);
slideToDate(getDateSkippingWeekend(calculatedDate));
changeDay.value = 0;
});
watch(changeDate, (date) => {
slideToDate(setUTCMidnight(date));
});
</script>
<template>
<swiper
:modules="swiperModules"
:slides-per-view="1"
:space-between="50"
:initial-slide="1"
:virtual="true"
@slide-change-transition-end="slideChangeEnd"
@slide-reset-transition-end="slideResetEnd"
@slide-change="slideChange"
@swiper="setSwiper"
>
<swiper-slide
v-for="(date, index) in loadedDates"
:key="index"
:virtualIndex="index"
>
<ScrollableContainer>
<component :is="element" :date="date" />
</ScrollableContainer>
</swiper-slide>
</swiper>
</template>
<style>
@import "swiper/css";
</style>
<style scoped>
.swiper {
padding: 5px 10px 0px 10px;
z-index: 0;
height: 100%;
box-sizing: border-box;
}
</style>