mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 21:33:18 +01:00
88 lines
2.5 KiB
Vue
88 lines
2.5 KiB
Vue
<template>
|
|
<div class="flex min-h-0 flex-col gap-2">
|
|
<div class="flex min-h-0 flex-1 flex-col overflow-auto overscroll-y-contain">
|
|
<div
|
|
ref="container"
|
|
class="scrollbar-hide flex shrink-0 grow snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth"
|
|
>
|
|
<component v-for="(card, index) in providedCards" :key="index" :is="card" ref="cards" />
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-col gap-2">
|
|
<h3 class="text-center text-sm font-thin">
|
|
{{ cards?.[activeIndex].title }}
|
|
</h3>
|
|
<div class="flex flex-none justify-center gap-2" v-if="providedCards.length > 1">
|
|
<button
|
|
v-for="(c, index) in providedCards"
|
|
:key="c.props?.id"
|
|
@click="scrollToItem(index)"
|
|
:class="[
|
|
'size-2 rounded-full transition-all duration-700',
|
|
activeIndex === index ? 'bg-primary scale-125' : 'bg-base-content/50 hover:bg-base-content',
|
|
]"
|
|
:aria-label="c.props?.title"
|
|
:title="c.props?.title"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import CarouselItem from "./CarouselItem.vue";
|
|
const container = useTemplateRef<HTMLDivElement>("container");
|
|
const activeIndex = ref(0);
|
|
const activeId = defineModel<string>();
|
|
const slots = defineSlots<{ default(): VNode[] }>();
|
|
const providedCards = computed(() => slots.default().filter(({ type }) => type === CarouselItem));
|
|
const cards = useTemplateRef<InstanceType<typeof CarouselItem>[]>("cards");
|
|
|
|
const scrollToItem = (index: number) => {
|
|
cards.value?.[index].$el.scrollIntoView({
|
|
behavior: "smooth",
|
|
inline: "start",
|
|
});
|
|
};
|
|
|
|
const { pause, resume } = watchPausable(activeId, (v) => {
|
|
if (activeId.value) {
|
|
const index = cards.value?.map((c) => c.id).indexOf(activeId.value) ?? -1;
|
|
if (index !== -1) {
|
|
scrollToItem(index);
|
|
}
|
|
}
|
|
});
|
|
|
|
useIntersectionObserver(
|
|
cards as Ref<InstanceType<typeof CarouselItem>[]>,
|
|
(entries) => {
|
|
entries.forEach(({ isIntersecting, target }) => {
|
|
if (isIntersecting) {
|
|
const index = cards.value?.map((c) => c.$el).indexOf(target as HTMLDivElement) ?? -1;
|
|
if (index !== -1) {
|
|
pause();
|
|
activeIndex.value = index;
|
|
activeId.value = cards.value?.[index].id;
|
|
nextTick(() => resume());
|
|
}
|
|
}
|
|
});
|
|
},
|
|
{
|
|
root: container,
|
|
threshold: 0.5,
|
|
},
|
|
);
|
|
</script>
|
|
|
|
<style scoped>
|
|
.scrollbar-hide {
|
|
scrollbar-width: none;
|
|
-ms-overflow-style: none;
|
|
}
|
|
.scrollbar-hide::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
</style>
|