Skip to content

Commit 2398f96

Browse files
committed
add sponsor carousel to homepage
1 parent 6b125b8 commit 2398f96

File tree

3 files changed

+152
-1
lines changed

3 files changed

+152
-1
lines changed

packages/frontendmu-nuxt/components/home/Hero.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<div class="homepage-container -mt-32 pt-16 md:pt-0">
66
<div class="homepage-wrapper">
77
<main>
8-
<div class="relative z-0 w-full contain py-[200px] flex flex-col gap-32">
8+
<div class="relative z-0 w-full contain pt-[200px] pb-[100px] flex flex-col gap-32">
99
<div class="relative z-20 flex flex-col-reverse md:flex-row h-full justify-between">
1010
<div class="flex flex-col justify-center text-center md:text-left gap-10 md:w-2/3">
1111
<h1
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<script setup lang="ts">
2+
import { computed } from 'vue'
3+
import { useSponsorStore } from '@/store/sponsorStore'
4+
5+
const sponsorStore = useSponsorStore()
6+
// Get all sponsors, sorted by their most recent event date descending
7+
const sortedSponsors = computed(() => {
8+
return [...sponsorStore.sponsors]
9+
.sort((a, b) => {
10+
// Find most recent meetup date for each sponsor
11+
const aLatest = a.meetups.reduce((max, m) => m.date > max ? m.date : max, '')
12+
const bLatest = b.meetups.reduce((max, m) => m.date > max ? m.date : max, '')
13+
return bLatest.localeCompare(aLatest)
14+
})
15+
})
16+
17+
function sponsorLogoUrl(sponsor: any) {
18+
if (typeof sponsor.logo === 'string') {
19+
return `https://directus.frontend.mu/assets/${sponsor.logo}`
20+
}
21+
return ''
22+
}
23+
24+
const carouselRef = ref<HTMLDivElement | null>(null)
25+
function scrollByOne(dir: 'left' | 'right') {
26+
if (!carouselRef.value)
27+
return
28+
const el = carouselRef.value
29+
const card = el.querySelector('.carousel-item') as HTMLElement
30+
if (!card)
31+
return
32+
const scrollAmount = card.offsetWidth + 16 // 16px gap
33+
el.scrollBy({
34+
left: dir === 'left' ? -scrollAmount : scrollAmount,
35+
behavior: 'smooth',
36+
})
37+
}
38+
</script>
39+
40+
<template>
41+
<section class="relative w-full max-w-6xl mx-auto py-6 md:py-10">
42+
<div class="flex items-center justify-between mb-3">
43+
<h2 class="text-2xl md:text-3xl font-bold text-verse-600 dark:text-verse-100">
44+
Our Sponsors
45+
</h2>
46+
<div class="flex gap-2">
47+
<button aria-label="Scroll left" class="carousel-arrow" @click="() => scrollByOne('left')">
48+
<Icon name="ic:round-arrow-back-ios" class="w-6 h-6" />
49+
</button>
50+
<button aria-label="Scroll right" class="carousel-arrow" @click="() => scrollByOne('right')">
51+
<Icon name="ic:round-arrow-forward-ios" class="w-6 h-6" />
52+
</button>
53+
</div>
54+
</div>
55+
<div
56+
ref="carouselRef"
57+
class="carousel flex gap-4 overflow-x-auto scroll-smooth snap-x snap-mandatory pb-4 px-4"
58+
tabindex="0"
59+
aria-label="Sponsor logos carousel"
60+
>
61+
<div
62+
v-for="sponsor in sortedSponsors"
63+
:key="sponsor.id"
64+
class="carousel-item flex-shrink-0 w-32 h-32 md:w-32 md:h-16 rounded-full bg-white dark:bg-verse-900 shadow-lg flex items-center justify-center snap-center transition-transform duration-300 p-2"
65+
:title="sponsor.name"
66+
>
67+
<img
68+
:src="sponsorLogoUrl(sponsor)"
69+
:alt="sponsor.name"
70+
class="w-full h-full object-contain rounded-full "
71+
:class="sponsor.darkbg ? 'dark:bg-verse-900 bg-gray-500' : 'bg-white'"
72+
draggable="false"
73+
>
74+
</div>
75+
</div>
76+
</section>
77+
</template>
78+
79+
<style scoped>
80+
.carousel {
81+
scroll-padding-left: 1rem;
82+
scroll-padding-right: 1rem;
83+
scrollbar-width: thin;
84+
}
85+
.carousel::-webkit-scrollbar {
86+
height: 12px;
87+
background: transparent;
88+
}
89+
.light-mode .carousel::-webkit-scrollbar-thumb {
90+
background: linear-gradient(90deg, #b6c2de 0%, #e0e7ef 100%);
91+
border-radius: 8px;
92+
border: 2px solid #f0f4fa;
93+
background-clip: padding-box;
94+
box-shadow: 0 2px 8px #cbd5e188;
95+
opacity: 0.7;
96+
transition: background 0.2s, opacity 0.3s;
97+
}
98+
.light-mode .carousel::-webkit-scrollbar-thumb:hover,
99+
.light-mode .carousel::-webkit-scrollbar-thumb:active {
100+
background: linear-gradient(90deg, #e0e7ef 0%, #b6c2de 100%);
101+
opacity: 1;
102+
}
103+
.light-mode .carousel::-webkit-scrollbar-track {
104+
background: #f8fafc;
105+
border-radius: 8px;
106+
}
107+
108+
.dark-mode .carousel::-webkit-scrollbar-thumb {
109+
background: linear-gradient(90deg, #7dd3fc 0%, #94a3b8 100%);
110+
border-radius: 8px;
111+
border: 3px solid #1e293b;
112+
background-clip: padding-box;
113+
box-shadow: 0 2px 8px #0f172a88;
114+
opacity: 0.7;
115+
transition: background 0.2s, opacity 0.3s;
116+
}
117+
.dark-mode .carousel::-webkit-scrollbar-thumb:hover,
118+
.dark-mode .carousel::-webkit-scrollbar-thumb:active {
119+
background: linear-gradient(90deg, #bae6fd 0%, #a5b4fc 100%);
120+
opacity: 1;
121+
}
122+
.dark-mode .carousel::-webkit-scrollbar-track {
123+
background: rgba(15, 23, 42, 0.3);
124+
border-radius: 8px;
125+
}
126+
127+
.carousel {
128+
scrollbar-width: thin;
129+
}
130+
.light-mode .carousel {
131+
scrollbar-color: #b6c2de #f8fafc;
132+
}
133+
.dark-mode .carousel {
134+
scrollbar-color: #7dd3fc #23293a;
135+
}
136+
137+
.carousel-arrow {
138+
@apply rounded-full bg-verse-100 dark:bg-verse-800 p-2 hover:bg-verse-200 dark:hover:bg-verse-700 shadow transition;
139+
outline: none;
140+
}
141+
.carousel-arrow:focus {
142+
@apply ring-2 ring-verse-400;
143+
}
144+
.carousel-item {
145+
transition: transform 0.3s cubic-bezier(.4,2,.6,1);
146+
}
147+
.carousel-item:active {
148+
transform: scale(0.96);
149+
}
150+
</style>

packages/frontendmu-nuxt/pages/index.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defineOgImageComponent('Home')
55
<template>
66
<div>
77
<HomeHero />
8+
<HomeSponsorCarousel />
89
<HomeLatestMeetup />
910
<HomeFeaturedSpeakers />
1011
<HomeSocialPresence />

0 commit comments

Comments
 (0)