Add RAR Storefront WordPress theme
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>
This commit is contained in:
230
theme/js/rar.js
Normal file
230
theme/js/rar.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* 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);
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user