371 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** @odoo-module **/
/**
* Aakriti Events Premium Theme V2 JS
* Handles: sticky header, mobile drawer, gallery carousel, scroll reveal, counter animation
*/
(function () {
'use strict';
/* ── Helpers ──────────────────────────────────────────── */
function qs(sel, ctx) { return (ctx || document).querySelector(sel); }
function qsa(sel, ctx) { return (ctx || document).querySelectorAll(sel); }
function onReady(fn) {
if (document.readyState !== 'loading') { fn(); }
else { document.addEventListener('DOMContentLoaded', fn); }
}
/* ── Sticky Header ────────────────────────────────────── */
function initHeader() {
var header = qs('#v2-header');
if (!header) return;
function updateHeader() {
if (window.scrollY > 40) {
header.classList.add('scrolled');
} else {
header.classList.remove('scrolled');
}
}
window.addEventListener('scroll', updateHeader, { passive: true });
updateHeader();
}
/* ── Mobile Drawer ────────────────────────────────────── */
function initMobileDrawer() {
var toggle = qs('#v2-mobile-toggle');
var drawer = qs('#v2-mobile-drawer');
var overlay = qs('#v2-drawer-overlay');
var closeBtn = qs('#v2-drawer-close');
if (!toggle || !drawer) return;
function openDrawer() {
drawer.classList.add('open');
if (overlay) { overlay.classList.add('active'); }
document.body.style.overflow = 'hidden';
}
function closeDrawer() {
drawer.classList.remove('open');
if (overlay) { overlay.classList.remove('active'); }
document.body.style.overflow = '';
}
toggle.addEventListener('click', openDrawer);
if (closeBtn) { closeBtn.addEventListener('click', closeDrawer); }
if (overlay) { overlay.addEventListener('click', closeDrawer); }
qsa('#v2-mobile-drawer a').forEach(function (a) {
a.addEventListener('click', closeDrawer);
});
window.addEventListener('resize', function () {
if (window.innerWidth > 991) { closeDrawer(); }
});
}
/* ── Gallery Carousel ─────────────────────────────────── */
function initGallery() {
var track = qs('#v2-gallery-track');
var prevBtn = qs('#v2-gallery-prev');
var nextBtn = qs('#v2-gallery-next');
if (!track) return;
var current = 0;
var itemWidth = 0;
var visibleItems = 4;
function updateLayout() {
var ww = window.innerWidth;
if (ww > 991) {
visibleItems = 4;
} else if (ww > 576) {
visibleItems = 3;
} else {
visibleItems = 2;
}
var containerWidth = track.parentElement.offsetWidth;
var gap = 12; // White gap between slanted items in pixels
var items = qsa('.v2-gallery-item-slant', track);
// Calculate width 'w' so that 'visibleItems' fit exactly in containerWidth
var w = (containerWidth - (visibleItems - 1) * gap) / (0.75 * visibleItems + 0.25);
var mr = (-0.25 * w) + gap;
items.forEach(function (item, idx) {
item.style.width = w + 'px';
if (idx === items.length - 1) {
item.style.marginRight = '0px';
} else {
item.style.marginRight = mr + 'px';
}
});
itemWidth = (w * 0.75) + gap; // The effective step width to slide
current = clamp(current, 0, getMax());
goTo(current, false);
updateButtons();
}
function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); }
function getMax() {
var itemsCount = track.children.length;
return Math.max(0, itemsCount - visibleItems);
}
function updateButtons() {
if (prevBtn) {
if (current <= 0) {
prevBtn.style.opacity = '0.5';
prevBtn.style.pointerEvents = 'none';
} else {
prevBtn.style.opacity = '1';
prevBtn.style.pointerEvents = 'auto';
}
}
if (nextBtn) {
if (current >= getMax()) {
nextBtn.style.opacity = '0.5';
nextBtn.style.pointerEvents = 'none';
} else {
nextBtn.style.opacity = '1';
nextBtn.style.pointerEvents = 'auto';
}
}
}
function goTo(index, animate) {
if (animate === undefined) animate = true;
current = clamp(index, 0, getMax());
track.style.transition = animate ? 'transform 0.4s ease' : 'none';
track.style.transform = 'translateX(-' + (current * itemWidth) + 'px)';
updateButtons();
}
if (prevBtn) { prevBtn.addEventListener('click', function () { goTo(current - 1); resetAutoSlide(); }); }
if (nextBtn) { nextBtn.addEventListener('click', function () { goTo(current + 1); resetAutoSlide(); }); }
// Touch/swipe support
var startX = 0;
track.addEventListener('touchstart', function (e) { startX = e.touches[0].clientX; }, { passive: true });
track.addEventListener('touchend', function (e) {
var diff = startX - e.changedTouches[0].clientX;
if (Math.abs(diff) > 50) {
goTo(current + (diff > 0 ? 1 : -1));
resetAutoSlide();
}
}, { passive: true });
// Auto slide
var autoSlideInterval;
function startAutoSlide() {
autoSlideInterval = setInterval(function () {
if (current >= getMax()) {
goTo(0);
} else {
goTo(current + 1);
}
}, 3000);
}
function resetAutoSlide() {
clearInterval(autoSlideInterval);
startAutoSlide();
}
window.addEventListener('resize', updateLayout);
updateLayout();
startAutoSlide();
}
/* ── Scroll Reveal ────────────────────────────────────── */
function initReveal() {
var els = qsa('.v2-reveal');
if (!els.length) return;
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
io.unobserve(entry.target);
}
});
}, { threshold: 0.12 });
els.forEach(function (el) { io.observe(el); });
} else {
els.forEach(function (el) { el.classList.add('visible'); });
}
}
/* ── Counter Animation ────────────────────────────────── */
function animateCounter(el, target, suffix) {
var start = 0;
var duration = 1800;
var startTime = null;
function step(ts) {
if (!startTime) startTime = ts;
var progress = Math.min((ts - startTime) / duration, 1);
var eased = 1 - Math.pow(1 - progress, 3); // ease out cubic
var val = Math.round(eased * target);
el.textContent = val + suffix;
if (progress < 1) { requestAnimationFrame(step); }
}
requestAnimationFrame(step);
}
function initCounters() {
var counters = qsa('[data-v2-counter]');
if (!counters.length) return;
var observed = false;
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting && !observed) {
observed = true;
counters.forEach(function (el) {
var target = parseInt(el.getAttribute('data-v2-counter'), 10);
var suffix = el.getAttribute('data-v2-suffix') || '';
animateCounter(el, target, suffix);
});
io.disconnect();
}
});
}, { threshold: 0.3 });
var statsSection = qs('#v2-stats');
if (statsSection) { io.observe(statsSection); }
} else {
counters.forEach(function (el) {
el.textContent = el.getAttribute('data-v2-counter') + (el.getAttribute('data-v2-suffix') || '');
});
}
}
/* ── Active nav link ──────────────────────────────────── */
function initActiveNav() {
var path = window.location.pathname;
qsa('.v2-nav a, .v2-drawer-nav a').forEach(function (a) {
var href = a.getAttribute('href') || '';
if (href === path || (href !== '/' && path.startsWith(href))) {
a.classList.add('active');
}
});
}
/* ── Service Tabs (Themes) ────────────────────────────── */
function initServiceTabs() {
var pillBtns = qsa('.svc-pill-btn');
var themePanels = qsa('.svc-fw-theme-panel');
if (!pillBtns.length || !themePanels.length) return;
pillBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
var targetId = btn.getAttribute('data-target');
if (!targetId) return;
// Remove active class from all buttons and panels
pillBtns.forEach(function (b) { b.classList.remove('active'); });
themePanels.forEach(function (p) { p.classList.remove('active'); });
// Add active class to clicked button and target panel
btn.classList.add('active');
var targetPanel = qs('#' + targetId);
if (targetPanel) {
targetPanel.classList.add('active');
}
});
});
}
/* ── Bootstrap ────────────────────────────────────────── */
function initPortfolio() {
const grid = document.getElementById('v2-portfolio-grid');
const tabs = document.querySelectorAll('.v2-portfolio-tab');
if (!grid || !tabs.length) return;
// Dynamic portfolio data
const portfolioData = [
{ category: 'weddings', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
{ category: 'corporate', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
{ category: 'birthday', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
{ category: 'cultural', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
{ category: 'private', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' },
{ category: 'weddings', img: '/theme_aakriti_v2/static/src/img/image.jpg', title: 'Lorem ipsum', location: 'Lorem ipsum' }
];
// Function to render items
function renderItems(filter) {
grid.innerHTML = ''; // Clear current
portfolioData.forEach(item => {
if (filter === 'all' || filter === item.category) {
const itemHTML = `
<div class="v2-portfolio-item" data-category="${item.category}" style="opacity: 0; transform: scale(0.9); transition: opacity 0.4s ease, transform 0.4s ease;">
<div class="v2-portfolio-item-inner">
<img src="${item.img}" alt="${item.title}"/>
<div class="v2-portfolio-info">
<p style="text-align: center;">Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
</div>
</div>
`;
// Parse and append to trigger animation
const temp = document.createElement('div');
temp.innerHTML = itemHTML.trim();
const element = temp.firstChild;
grid.appendChild(element);
// Trigger animation after append
setTimeout(() => {
element.style.opacity = '1';
element.style.transform = 'scale(1)';
}, 50);
}
});
}
// Initial render
renderItems('all');
// Tab click handlers
tabs.forEach(tab => {
tab.addEventListener('click', function () {
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
const filter = this.getAttribute('data-filter');
// Fade out existing items
const currentItems = grid.querySelectorAll('.v2-portfolio-item');
currentItems.forEach(item => {
item.style.opacity = '0';
item.style.transform = 'scale(0.9)';
});
// Wait for fade out, then render new filtered list
setTimeout(() => {
renderItems(filter);
}, 400); // Wait for transition
});
});
}
onReady(function () {
initHeader();
initMobileDrawer();
initGallery();
initReveal();
initCounters();
initActiveNav();
initServiceTabs();
initPortfolio();
});
})();