Initial commit
This commit is contained in:
commit
c38fde40b0
33 changed files with 7319 additions and 0 deletions
171
assets/js/home.js
Normal file
171
assets/js/home.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
// 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(); });
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue