website/assets/js/home.js

171 lines
No EOL
6.1 KiB
JavaScript

// Home page specific JS: team carousel + modal
document.addEventListener('DOMContentLoaded', () => {
// Fetch team data and render
fetch('assets/data/team.json')
.then(res => res.json())
.then(members => {
initTeamSection(members);
})
.catch(err => {
console.error('Failed to load team data', err);
});
function initTeamSection(members) {
const carousel = document.getElementById('team-carousel');
const dotsContainer = document.getElementById('carousel-dots');
const prevBtn = document.getElementById('carousel-prev');
const nextBtn = document.getElementById('carousel-next');
// Render cards
carousel.innerHTML = members.map((m, i) => `
<div class="team-card" data-member="${i}">
<div class="team-avatar">
<div class="team-avatar-inner">${m.avatar}</div>
</div>
<h4>${m.name}</h4>
<p class="team-role">${m.role}</p>
<p class="team-desc">${(m.bio || '').split('.').shift()}.</p>
</div>
`).join('');
// Carousel logic
const cardsContainer = carousel; // the track
let cards = carousel.querySelectorAll('.team-card');
let currentIndex = 0;
let cardsPerView = 3;
let autoPlayInterval;
function updateCardsPerView() {
if (window.innerWidth <= 768) cardsPerView = 1;
else if (window.innerWidth <= 1024) cardsPerView = 2;
else cardsPerView = 3;
}
function createDots() {
dotsContainer.innerHTML = '';
const totalDots = Math.max(1, Math.ceil(cards.length / cardsPerView));
for (let i = 0; i < totalDots; i++) {
const dot = document.createElement('div');
dot.classList.add('carousel-dot');
if (i === 0) dot.classList.add('active');
dot.addEventListener('click', () => goToSlide(i));
dotsContainer.appendChild(dot);
}
}
function updateCarousel() {
cards = carousel.querySelectorAll('.team-card');
if (cards.length === 0) return;
const cardStyle = window.getComputedStyle(cards[0]);
const gap = 32; // matches previous implementation
const cardWidth = cards[0].offsetWidth + gap;
carousel.style.transform = `translateX(-${currentIndex * cardWidth}px)`;
const dots = dotsContainer.querySelectorAll('.carousel-dot');
const activeDotIndex = Math.floor(currentIndex / cardsPerView);
dots.forEach((dot, i) => dot.classList.toggle('active', i === activeDotIndex));
}
function goToSlide(dotIndex) {
currentIndex = dotIndex * cardsPerView;
if (currentIndex > cards.length - cardsPerView) currentIndex = Math.max(0, cards.length - cardsPerView);
updateCarousel();
resetAutoPlay();
}
function nextSlide() {
currentIndex++;
if (currentIndex > cards.length - cardsPerView) currentIndex = 0;
updateCarousel();
}
function prevSlide() {
currentIndex--;
if (currentIndex < 0) currentIndex = Math.max(0, cards.length - cardsPerView);
updateCarousel();
}
function startAutoPlay() {
clearInterval(autoPlayInterval);
autoPlayInterval = setInterval(nextSlide, 4000);
}
function resetAutoPlay() {
clearInterval(autoPlayInterval);
startAutoPlay();
}
prevBtn && prevBtn.addEventListener('click', () => { prevSlide(); resetAutoPlay(); });
nextBtn && nextBtn.addEventListener('click', () => { nextSlide(); resetAutoPlay(); });
carousel.addEventListener('mouseenter', () => clearInterval(autoPlayInterval));
carousel.addEventListener('mouseleave', startAutoPlay);
updateCardsPerView();
createDots();
startAutoPlay();
window.addEventListener('resize', () => {
updateCardsPerView();
createDots();
currentIndex = 0;
updateCarousel();
});
// Modal logic
const modalOverlay = document.getElementById('team-modal-overlay');
const modalClose = document.getElementById('team-modal-close');
const modalAvatar = document.getElementById('modal-avatar');
const modalName = document.getElementById('modal-name');
const modalRole = document.getElementById('modal-role');
const modalBio = document.getElementById('modal-bio');
const modalStats = document.getElementById('modal-stats');
const modalSkills = document.getElementById('modal-skills');
const modalSocial = document.getElementById('modal-social');
function openMemberModal(index) {
const member = members[index];
if (!member) return;
modalAvatar.textContent = member.avatar || member.name.charAt(0);
modalName.textContent = member.name;
modalRole.textContent = member.role;
modalBio.textContent = member.bio;
modalStats.innerHTML = (member.stats || []).map(stat => `
<div class="team-modal-stat">
<div class="team-modal-stat-value">${stat.value}</div>
<div class="team-modal-stat-label">${stat.label}</div>
</div>
`).join('');
modalSkills.innerHTML = (member.skills || []).map(s => `<span class="team-modal-skill">${s}</span>`).join('');
modalSocial.innerHTML = `
<a href="#" aria-label="Discord"><i data-feather="message-circle"></i></a>
<a href="#" aria-label="Twitter"><i data-feather="twitter"></i></a>
`;
if (window.feather && feather.replace) feather.replace();
modalOverlay.classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeMemberModal() {
modalOverlay.classList.remove('active');
document.body.style.overflow = '';
}
// Attach click listeners to generated cards
carousel.querySelectorAll('.team-card').forEach(card => {
card.addEventListener('click', () => {
const idx = parseInt(card.dataset.member, 10);
openMemberModal(idx);
});
});
modalClose && modalClose.addEventListener('click', closeMemberModal);
modalOverlay && modalOverlay.addEventListener('click', (e) => { if (e.target === modalOverlay) closeMemberModal(); });
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay.classList.contains('active')) closeMemberModal(); });
}
});