144 lines
3.9 KiB
Vue
144 lines
3.9 KiB
Vue
<template>
|
|
<div v-show="showSwiper" class="relative" :class="rootClasses">
|
|
<swiper
|
|
loop-add-blank-slides
|
|
:class="sliderClasses"
|
|
:direction="direction"
|
|
:breakpoints="{
|
|
450: { slidesPerView: 1.2, spaceBetween: 20 },
|
|
500: { slidesPerView: 1.5, spaceBetween: 20 },
|
|
600: { slidesPerView: 1.5, spaceBetween: 20 },
|
|
770: { slidesPerView: 1, spaceBetween: 20 },
|
|
800: { slidesPerView: 1, spaceBetween: 20 },
|
|
1000: { slidesPerView: 1.2, spaceBetween: 20 },
|
|
1200: { slidesPerView: 1.5, spaceBetween: 20 },
|
|
1600: { slidesPerView: 2, spaceBetween: 40 },
|
|
2000: { slidesPerView: 2.5, spaceBetween: 40 },
|
|
}"
|
|
:space-between="50"
|
|
@swiper="onSwiper"
|
|
@slide-change="onSlideChange"
|
|
>
|
|
<swiper-slide
|
|
v-for="item in items"
|
|
:key="item.title"
|
|
class="hover:cursor-pointer"
|
|
>
|
|
<slot :item="item" />
|
|
</swiper-slide>
|
|
</swiper>
|
|
<div>
|
|
<div :class="counterClasses">
|
|
<div :class="progressClass" :style="`width: ${progerssBar}%`"></div>
|
|
<div class="flex justify-between my-4">
|
|
<button
|
|
:disabled="isPrevDisabled"
|
|
class="px-4 py-2 disabled:text-gray-400 cursor-pointer"
|
|
@click="
|
|
useRipple($event, true);
|
|
goPrev();
|
|
"
|
|
>
|
|
<ArrowLeftIcon class="size-5" />
|
|
</button>
|
|
<button
|
|
:disabled="isNextDisabled"
|
|
class="px-4 py-2 disabled:text-gray-400 cursor-pointer"
|
|
@click="
|
|
useRipple($event, true);
|
|
goNext();
|
|
"
|
|
>
|
|
<ArrowRightIcon class="size-5" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// import { useRipple } from "#imports";
|
|
import { ref, computed, defineProps, withDefaults } from "vue";
|
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
|
import type { Swiper as swiper } from "swiper/types";
|
|
import "swiper/css";
|
|
import { twMerge } from "tailwind-merge";
|
|
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/vue/24/solid";
|
|
import type { PTAttribs, SliderProps } from "./types";
|
|
|
|
const showSwiper = ref(false);
|
|
const progerssBar = ref(0);
|
|
const prevDisabled = ref(true);
|
|
const nextDisabled = ref(true);
|
|
|
|
let swiperInstance: swiper;
|
|
|
|
const onSwiper = (swiper: swiper) => {
|
|
progerssBar.value = Math.ceil(
|
|
((swiper.realIndex + (swiper.params.slidesPerView as number)) /
|
|
swiper.el.querySelectorAll(".swiper-slide").length) *
|
|
100
|
|
);
|
|
swiperInstance = swiper;
|
|
showSwiper.value = true;
|
|
prevDisabled.value = !swiper.params.loop && swiper.realIndex == 0;
|
|
nextDisabled.value = swiperInstance.isEnd;
|
|
};
|
|
|
|
const onSlideChange = (swiper: swiper) => {
|
|
progerssBar.value = Math.ceil(
|
|
((swiper.realIndex + (swiper.params.slidesPerView as number)) /
|
|
swiper.slides.length) *
|
|
100
|
|
);
|
|
prevDisabled.value = !swiper.params.loop && swiper.realIndex == 0;
|
|
nextDisabled.value = swiper.isEnd;
|
|
};
|
|
|
|
const isPrevDisabled = computed(() => prevDisabled.value);
|
|
const isNextDisabled = computed(() => nextDisabled.value);
|
|
|
|
function goNext() {
|
|
swiperInstance.slideNext();
|
|
}
|
|
|
|
function goPrev() {
|
|
swiperInstance.slidePrev();
|
|
}
|
|
|
|
const props = withDefaults(defineProps<SliderProps>(), {
|
|
items: [],
|
|
});
|
|
|
|
const classes = ref({
|
|
root: {
|
|
class: ``,
|
|
},
|
|
slider: {
|
|
class: ``,
|
|
},
|
|
counter: {
|
|
class: ``,
|
|
},
|
|
progress: {
|
|
class: "",
|
|
},
|
|
} as unknown as PTAttribs);
|
|
|
|
const rootClasses = computed(() =>
|
|
twMerge(classes.value.root?.class, props.pt?.root?.class)
|
|
);
|
|
const sliderClasses = computed(() =>
|
|
twMerge(classes.value.slider?.class, props.pt?.slider?.class)
|
|
);
|
|
const counterClasses = computed(() =>
|
|
twMerge(classes.value.counter?.class, props.pt?.counter?.class)
|
|
);
|
|
const progressClass = computed(() =>
|
|
twMerge(classes.value.progress?.class, props.pt?.progress?.class)
|
|
);
|
|
|
|
const items = props.items;
|
|
</script>
|