Implements the Reply All Regrets™ frontend design: retro office aesthetic with manila/ink/stamp-red/olive palette, Fraunces + JetBrains Mono + Inter typography. Includes homepage, category, product detail, blog, about, cart templates with WooCommerce integration and interactive JS (filter, sort, gallery). Co-Authored-By: Paperclip <noreply@paperclip.ing>
231 lines
9.3 KiB
JavaScript
231 lines
9.3 KiB
JavaScript
/**
|
|
* Reply All Regrets — Interactive features
|
|
* Filter sidebar, sort, view toggle, gallery switcher, qty controls
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
// ─── Gallery switcher ────────────────────────────────────────────────────
|
|
window.rarSwitchGallery = function (index, url) {
|
|
var img = document.getElementById('rar-gallery-img');
|
|
var counter = document.getElementById('rar-gallery-counter');
|
|
var label = document.getElementById('rar-gallery-label');
|
|
var thumbs = document.querySelectorAll('.rar-gallery__thumb');
|
|
|
|
if (img) img.src = url;
|
|
if (counter) counter.textContent = index + 1;
|
|
if (label) label.textContent = 'FIG. ' + (index + 1) + ' — PRODUCT VIEW';
|
|
|
|
thumbs.forEach(function (t, i) {
|
|
t.classList.toggle('active', i === index);
|
|
});
|
|
};
|
|
|
|
// ─── Grid / List view toggle ─────────────────────────────────────────────
|
|
window.rarSetView = function (view) {
|
|
var grid = document.getElementById('rar-product-grid');
|
|
var gridBtn = document.getElementById('rar-grid-btn');
|
|
var listBtn = document.getElementById('rar-list-btn');
|
|
|
|
if (!grid) return;
|
|
|
|
if (view === 'list') {
|
|
grid.classList.add('list-view');
|
|
if (gridBtn) gridBtn.classList.remove('active');
|
|
if (listBtn) listBtn.classList.add('active');
|
|
} else {
|
|
grid.classList.remove('list-view');
|
|
if (gridBtn) gridBtn.classList.add('active');
|
|
if (listBtn) listBtn.classList.remove('active');
|
|
}
|
|
|
|
try { localStorage.setItem('rar-view', view); } catch (e) {}
|
|
};
|
|
|
|
// Restore saved view preference
|
|
(function () {
|
|
try {
|
|
var saved = localStorage.getItem('rar-view');
|
|
if (saved === 'list') { rarSetView('list'); }
|
|
} catch (e) {}
|
|
})();
|
|
|
|
// ─── Sort menu close on outside click ────────────────────────────────────
|
|
document.addEventListener('click', function (e) {
|
|
var sortMenu = document.getElementById('rar-sort-menu');
|
|
var sortBtn = document.querySelector('.rar-sort-btn');
|
|
if (sortMenu && !sortMenu.contains(e.target) && sortBtn && !sortBtn.contains(e.target)) {
|
|
sortMenu.classList.remove('open');
|
|
}
|
|
});
|
|
|
|
// ─── Filter: category/attribute ──────────────────────────────────────────
|
|
window.rarApplyFilter = function (checkbox) {
|
|
var url = new URL(window.location.href);
|
|
if (checkbox.checked) {
|
|
url.searchParams.set(checkbox.name, checkbox.value);
|
|
} else {
|
|
url.searchParams.delete(checkbox.name);
|
|
}
|
|
window.location.href = url.toString();
|
|
};
|
|
|
|
window.rarPriceFilter = function (checkbox) {
|
|
var url = new URL(window.location.href);
|
|
// Uncheck siblings
|
|
document.querySelectorAll('[id^="price-"]').forEach(function (c) {
|
|
if (c !== checkbox) c.checked = false;
|
|
});
|
|
if (checkbox.checked) {
|
|
url.searchParams.set('min_price', checkbox.dataset.min);
|
|
url.searchParams.set('max_price', checkbox.dataset.max);
|
|
} else {
|
|
url.searchParams.delete('min_price');
|
|
url.searchParams.delete('max_price');
|
|
}
|
|
window.location.href = url.toString();
|
|
};
|
|
|
|
window.rarStockFilter = function (checkbox) {
|
|
var url = new URL(window.location.href);
|
|
if (checkbox.checked) {
|
|
url.searchParams.set('availability', 'instock');
|
|
} else {
|
|
url.searchParams.delete('availability');
|
|
}
|
|
window.location.href = url.toString();
|
|
};
|
|
|
|
window.rarApplyAllFilters = function () {
|
|
// Collect all checked filters and navigate
|
|
var url = new URL(window.location.href);
|
|
document.querySelectorAll('.rar-filter-item input[type=checkbox]:checked').forEach(function (c) {
|
|
if (c.name && c.value) url.searchParams.set(c.name, c.value);
|
|
});
|
|
window.location.href = url.toString();
|
|
};
|
|
|
|
// ─── Designs index: tag filter ──────────────────────────────────────────
|
|
window.rarFilterDesigns = function (tag, btn) {
|
|
var cards = document.querySelectorAll('[data-design-tag]');
|
|
var buttons = document.querySelectorAll('.rar-design-filter-btn');
|
|
|
|
buttons.forEach(function (b) { b.classList.remove('active'); });
|
|
btn.classList.add('active');
|
|
|
|
cards.forEach(function (card) {
|
|
if (tag === 'All' || card.dataset.designTag === tag) {
|
|
card.style.display = '';
|
|
} else {
|
|
card.style.display = 'none';
|
|
}
|
|
});
|
|
};
|
|
|
|
// ─── Qty controls ────────────────────────────────────────────────────────
|
|
document.addEventListener('click', function (e) {
|
|
if (e.target.matches('.rar-qty__btn')) {
|
|
var wrap = e.target.closest('.rar-qty');
|
|
if (!wrap) return;
|
|
var display = wrap.querySelector('.rar-qty__val');
|
|
var input = wrap.querySelector('input[type=number], input.qty');
|
|
var current = parseInt(display ? display.textContent : (input ? input.value : 1)) || 1;
|
|
|
|
if (e.target.dataset.action === 'plus') {
|
|
current++;
|
|
} else if (e.target.dataset.action === 'minus') {
|
|
current = Math.max(1, current - 1);
|
|
}
|
|
|
|
if (display) display.textContent = current;
|
|
if (input) {
|
|
input.value = current;
|
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
}
|
|
}
|
|
});
|
|
|
|
// ─── Nav: highlight current section ──────────────────────────────────────
|
|
(function () {
|
|
var path = window.location.pathname;
|
|
var links = document.querySelectorAll('.rar-nav__links a');
|
|
links.forEach(function (link) {
|
|
var href = link.getAttribute('href') || '';
|
|
if (href && href !== '/' && path.indexOf(href) === 0) {
|
|
link.style.color = 'var(--stamp)';
|
|
}
|
|
});
|
|
})();
|
|
|
|
// ─── Cart count update via WooCommerce fragments ──────────────────────────
|
|
document.addEventListener('wc_fragments_refreshed', function () {
|
|
// WooCommerce will update .rar-nav__cart via the fragments filter in functions.php
|
|
});
|
|
|
|
// ─── Announcement bar close ───────────────────────────────────────────────
|
|
var bar = document.querySelector('.rar-bar');
|
|
if (bar) {
|
|
bar.style.cursor = 'pointer';
|
|
bar.title = 'Click to dismiss';
|
|
bar.addEventListener('click', function () {
|
|
bar.style.display = 'none';
|
|
try { sessionStorage.setItem('rar-bar-dismissed', '1'); } catch (e) {}
|
|
});
|
|
try {
|
|
if (sessionStorage.getItem('rar-bar-dismissed') === '1') bar.style.display = 'none';
|
|
} catch (e) {}
|
|
}
|
|
|
|
// ─── WooCommerce quantity controls (override default +/-) ─────────────────
|
|
// Replace WC's quantity input with our styled controls
|
|
function initQtyControls() {
|
|
document.querySelectorAll('.quantity:not(.rar-qty-init)').forEach(function (wrap) {
|
|
var input = wrap.querySelector('input.qty, input[type=number]');
|
|
if (!input) return;
|
|
wrap.classList.add('rar-qty-init');
|
|
|
|
var minusBtn = document.createElement('button');
|
|
minusBtn.type = 'button';
|
|
minusBtn.className = 'rar-qty__btn';
|
|
minusBtn.dataset.action = 'minus';
|
|
minusBtn.innerHTML = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14"></path></svg>';
|
|
minusBtn.style.cssText = 'padding:0 14px;border-right:1px solid var(--ink);display:flex;align-items:center;';
|
|
|
|
var plusBtn = document.createElement('button');
|
|
plusBtn.type = 'button';
|
|
plusBtn.className = 'rar-qty__btn';
|
|
plusBtn.dataset.action = 'plus';
|
|
plusBtn.innerHTML = '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14M5 12h14"></path></svg>';
|
|
plusBtn.style.cssText = 'padding:0 14px;border-left:1px solid var(--ink);display:flex;align-items:center;';
|
|
|
|
input.style.cssText = 'padding:0 18px;font-family:var(--mono);font-size:14px;min-width:50px;text-align:center;border:none;background:transparent;';
|
|
|
|
wrap.style.cssText = 'display:flex;align-items:stretch;border:1.5px solid var(--ink);';
|
|
wrap.insertBefore(minusBtn, input);
|
|
wrap.appendChild(plusBtn);
|
|
|
|
[minusBtn, plusBtn].forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
var val = parseInt(input.value) || 1;
|
|
var step = parseInt(input.step) || 1;
|
|
if (btn.dataset.action === 'plus') {
|
|
input.value = val + step;
|
|
} else {
|
|
input.value = Math.max(parseInt(input.min) || 1, val - step);
|
|
}
|
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Run on DOM ready and after any AJAX
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initQtyControls);
|
|
} else {
|
|
initQtyControls();
|
|
}
|
|
document.addEventListener('wc_fragments_refreshed', initQtyControls);
|
|
|
|
})();
|