283 lines
12 KiB
Plaintext
283 lines
12 KiB
Plaintext
<div class="container max-w-6xl mx-auto py-20 px-4">
|
|
<div class="flex flex-col md:flex-row items-center md:items-start gap-10">
|
|
<!-- Left column -->
|
|
<div class="md:w-1/3 md:sticky md:top-24 md:self-start text-center md:text-left" data-aos="fade-right" data-aos-duration="800" data-aos-disable="mobile">
|
|
<div class="inline-flex items-center bg-oren text-white px-5 py-2 rounded-full shadow-md mb-6">
|
|
<i class="w-5 h-5 mr-2 text-white" data-lucide="medal"></i>
|
|
<h2 class="text-sm font-semibold">MITRA KAMI</h2>
|
|
</div>
|
|
|
|
<h3 class="text-2xl md:text-3xl font-bold text-gray-800 mb-6">Mitra of The Year</h3>
|
|
|
|
<p class="text-gray-600 text-balance mb-8">
|
|
Mitra terpilih yang telah memberikan kontribusi luar biasa dan bantuan berkelanjutan dalam mendukung program-program Dinas Lingkungan Hidup DKI Jakarta.
|
|
</p>
|
|
|
|
<div class="flex justify-center md:justify-start space-x-3 mb-8 md:mb-0">
|
|
<button id="prev-mitra-btn" class="p-2 rounded-full border border-gray-300 hover:border-orange-500 hover:text-orange-500 transition-colors duration-300">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
|
</svg>
|
|
</button>
|
|
|
|
<button id="next-mitra-btn" class="p-2 rounded-full border border-gray-300 hover:border-orange-500 hover:text-orange-500 transition-colors duration-300">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
</svg>
|
|
</button>
|
|
|
|
<!-- Dots indicator -->
|
|
<div class="flex items-center space-x-1.5 ml-4" id="mitra-dots">
|
|
<!-- Dots will be created dynamically -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right column: Mitra cards slider -->
|
|
<div class="md:w-2/3 relative" data-aos="fade-left" data-aos-duration="1000" data-aos-disable="mobile">
|
|
<div class="overflow-hidden">
|
|
<div class="flex transition-all duration-500 ease-out" id="slider-mitra">
|
|
<!-- Rows of mitra cards will be rendered here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<register-block dynamic-section="scripts" key="jsMitra">
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
if (window.innerWidth < 768) {
|
|
// Disable AOS on mobile
|
|
const elements = document.querySelectorAll('[data-aos-disable="mobile"]');
|
|
elements.forEach(element => {
|
|
element.setAttribute('data-aos', 'none');
|
|
});
|
|
} else {
|
|
// Enable AOS on desktop
|
|
const elements = document.querySelectorAll('[data-aos-disable="mobile"]');
|
|
elements.forEach(element => {
|
|
element.setAttribute('data-aos', 'fade-down');
|
|
});
|
|
}
|
|
// Mitra data
|
|
const mitraData = [
|
|
{
|
|
name: "PT. Mitra Sejahtera",
|
|
logo: "@Url.Content("~/assets/images/home/pertamina-retail.png")",
|
|
contributions: "150+ bantuan",
|
|
contributionColor: "text-ijo"
|
|
},
|
|
{
|
|
name: "CV. Media Partner",
|
|
logo: "@Url.Content("~/assets/images/home/pertamina-retail.png")",
|
|
contributions: "80+ bantuan",
|
|
contributionColor: "text-ijo"
|
|
},
|
|
{
|
|
name: "PT. Teknologi Maju",
|
|
logo: "@Url.Content("~/assets/images/home/pertamina-retail.png")",
|
|
contributions: "20+ bantuan",
|
|
contributionColor: "text-ijo"
|
|
},
|
|
{
|
|
name: "PT. Eco Solutions",
|
|
logo: "@Url.Content("~/assets/images/home/pertamina-retail.png")",
|
|
contributions: "50+ bantuan",
|
|
contributionColor: "text-ijo"
|
|
},
|
|
{
|
|
name: "PT. Green Energy",
|
|
logo: "@Url.Content("~/assets/images/home/pertamina-retail.png")",
|
|
contributions: "95+ bantuan",
|
|
contributionColor: "text-ijo"
|
|
},
|
|
{
|
|
name: "CV. Sustainable Future",
|
|
logo: "@Url.Content("~/assets/images/home/pertamina-retail.png")",
|
|
contributions: "65+ bantuan",
|
|
contributionColor: "text-ijo"
|
|
}
|
|
];
|
|
|
|
// Configuration
|
|
const isMobile = window.innerWidth < 768;
|
|
const cardsPerSlide = isMobile ? 1 : 3; // Show 1 card on mobile, 3 on desktop
|
|
const slider = document.getElementById('slider-mitra');
|
|
const dotsContainer = document.getElementById('mitra-dots');
|
|
const nextBtn = document.getElementById('next-mitra-btn');
|
|
const prevBtn = document.getElementById('prev-mitra-btn');
|
|
|
|
// Calculate how many slides we need
|
|
const totalSlides = Math.ceil(mitraData.length / cardsPerSlide);
|
|
|
|
// Generate slides with horizontal logos
|
|
for (let i = 0; i < totalSlides; i++) {
|
|
const slideDiv = document.createElement('div');
|
|
slideDiv.className = 'flex-shrink-0 w-full';
|
|
|
|
// Create a flex container for horizontal layout
|
|
const flexContainer = document.createElement('div');
|
|
// Add gap-2 for mobile to prevent overflow
|
|
flexContainer.className = `flex flex-row justify-between ${isMobile ? 'gap-2' : 'gap-6'}`;
|
|
|
|
// Get mitras for this slide
|
|
const startIndex = i * cardsPerSlide;
|
|
const slideMitras = mitraData.slice(startIndex, startIndex + cardsPerSlide);
|
|
|
|
// Create cards for this slide
|
|
slideMitras.forEach(mitra => {
|
|
const card = document.createElement('div');
|
|
card.className = 'mitra-card bg-white rounded-xl p-5 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 flex-1';
|
|
|
|
card.innerHTML = `
|
|
<div class="text-center">
|
|
<img src="${mitra.logo}" alt="${mitra.name}" class="w-24 h-24 mx-auto object-contain mb-4 rounded-lg">
|
|
<h3 class="font-bold text-gray-800 text-sm">${mitra.name}</h3>
|
|
<div class="bg-orange-100 text-orange-800 text-xs font-medium px-2 py-0.5 rounded-full inline-block my-2">
|
|
Mitra of the Year
|
|
</div>
|
|
<p class="text-gray-600 text-xs leading-relaxed">
|
|
<span class="font-semibold ${mitra.contributionColor}">${mitra.contributions}</span>
|
|
</p>
|
|
<button class="mt-3 text-orange-600 hover:text-orange-800 text-xs font-medium flex items-center justify-center mx-auto transition-colors duration-300">
|
|
Telusuri
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
flexContainer.appendChild(card);
|
|
});
|
|
|
|
// If we have fewer than cardsPerSlide, add empty placeholders to maintain layout
|
|
if (slideMitras.length < cardsPerSlide) {
|
|
for (let j = slideMitras.length; j < cardsPerSlide; j++) {
|
|
const placeholder = document.createElement('div');
|
|
placeholder.className = 'flex-1';
|
|
flexContainer.appendChild(placeholder);
|
|
}
|
|
}
|
|
|
|
slideDiv.appendChild(flexContainer);
|
|
slider.appendChild(slideDiv);
|
|
}
|
|
|
|
// Create navigation dots
|
|
for (let i = 0; i < totalSlides; i++) {
|
|
const dot = document.createElement('button');
|
|
dot.className = `w-2 h-2 rounded-full transition-all duration-300 ${i === 0 ? 'bg-oren w-6' : 'bg-gray-300'}`;
|
|
dot.setAttribute('data-index', i);
|
|
dot.onclick = () => goToSlide(i);
|
|
dotsContainer.appendChild(dot);
|
|
}
|
|
|
|
// Slider functionality
|
|
let currentSlide = 0;
|
|
|
|
function goToSlide(index) {
|
|
if (index < 0 || index >= totalSlides || index === currentSlide) return;
|
|
|
|
// Use GSAP for smooth animation
|
|
gsap.to(slider, {
|
|
x: `-${index * 100}%`,
|
|
duration: 0.6,
|
|
ease: "power2.out"
|
|
});
|
|
|
|
currentSlide = index;
|
|
updateDots();
|
|
updateButtons();
|
|
}
|
|
|
|
function updateDots() {
|
|
const dots = dotsContainer.querySelectorAll('button');
|
|
dots.forEach((dot, index) => {
|
|
if (index === currentSlide) {
|
|
dot.classList.add('bg-orange-600', 'w-6');
|
|
dot.classList.remove('bg-gray-300');
|
|
} else {
|
|
dot.classList.add('bg-gray-300');
|
|
dot.classList.remove('bg-orange-600', 'w-6');
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateButtons() {
|
|
prevBtn.disabled = currentSlide === 0;
|
|
nextBtn.disabled = currentSlide === totalSlides - 1;
|
|
|
|
prevBtn.style.opacity = currentSlide === 0 ? '0.5' : '1';
|
|
nextBtn.style.opacity = currentSlide === totalSlides - 1 ? '0.5' : '1';
|
|
}
|
|
|
|
// Navigation
|
|
nextBtn.addEventListener('click', () => {
|
|
goToSlide(currentSlide + 1);
|
|
});
|
|
|
|
prevBtn.addEventListener('click', () => {
|
|
goToSlide(currentSlide - 1);
|
|
});
|
|
|
|
// Handle window resize to adjust for orientation changes
|
|
window.addEventListener('resize', function() {
|
|
// Recalculate isMobile
|
|
const wasMobile = isMobile;
|
|
const newIsMobile = window.innerWidth < 768;
|
|
|
|
// If mobile state changed, reload the page to rebuild the carousel
|
|
if (wasMobile !== newIsMobile) {
|
|
location.reload();
|
|
}
|
|
});
|
|
|
|
// Add touch swipe support for mobile
|
|
if (isMobile) {
|
|
let touchStartX = 0;
|
|
let touchEndX = 0;
|
|
|
|
slider.addEventListener('touchstart', function(e) {
|
|
touchStartX = e.changedTouches[0].screenX;
|
|
}, {passive: true});
|
|
|
|
slider.addEventListener('touchend', function(e) {
|
|
touchEndX = e.changedTouches[0].screenX;
|
|
handleSwipe();
|
|
}, {passive: true});
|
|
|
|
function handleSwipe() {
|
|
if (touchEndX < touchStartX - 50) {
|
|
// Swipe left - go to next slide
|
|
goToSlide(currentSlide + 1);
|
|
}
|
|
if (touchEndX > touchStartX + 50) {
|
|
// Swipe right - go to previous slide
|
|
goToSlide(currentSlide - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add card entrance animation
|
|
const cards = document.querySelectorAll('.mitra-card');
|
|
gsap.fromTo(cards,
|
|
{ y: 30, opacity: 0 },
|
|
{
|
|
y: 0,
|
|
opacity: 1,
|
|
duration: 0.8,
|
|
stagger: 0.1,
|
|
ease: "power2.out",
|
|
delay: 0.3
|
|
}
|
|
);
|
|
|
|
// Initialize buttons state
|
|
updateButtons();
|
|
});
|
|
</script>
|
|
</register-block> |