(() => {
const root = document.querySelector('[data-mm]');
if (!root) return;
const min = parseInt(root.dataset.min || '2', 10);
const max = parseInt(root.dataset.max || '4', 10);
const pickButtons = Array.from(root.querySelectorAll('[data-mm-pick]'));
const selectedWrap = root.querySelector('[data-mm-selected]');
const addBtn = root.querySelector('[data-mm-add]');
const noteEl = root.querySelector('[data-mm-note]');
const metaEl = root.querySelector('[data-mm-meta]');
const errEl = root.querySelector('[data-mm-err]');
/** state */
const selected = new Map(); // variantId -> {title, image}
function setError(msg = '') {
if (!errEl) return;
if (!msg) {
errEl.hidden = true;
errEl.textContent = '';
} else {
errEl.hidden = false;
errEl.textContent = msg;
}
}
function updateUI() {
// highlight selected thumbs
pickButtons.forEach(btn => {
const id = btn.dataset.variantId;
btn.classList.toggle('is-selected', selected.has(id));
btn.setAttribute('aria-pressed', selected.has(id) ? 'true' : 'false');
});
// render selected (right side)
selectedWrap.innerHTML = '';
if (selected.size === 0) {
selectedWrap.innerHTML = `
`;
selectedWrap.appendChild(el);
}
}
// meta text
metaEl.textContent = `${selected.size} selected`;
// hint / rules
if (selected.size >= max) {
noteEl.textContent = `Max ${max} reached`;
} else {
noteEl.textContent = '';
}
// enable add
addBtn.disabled = selected.size < min || selected.size > max;
// clear errors when valid
if (!addBtn.disabled) setError('');
}
function escapeHtml(str) {
return String(str || '')
.replaceAll('&', '&')
.replaceAll('<', '<')
.replaceAll('>', '>')
.replaceAll('"', '"')
.replaceAll("'", ''');
}
function togglePick(btn) {
const variantId = btn.dataset.variantId;
const title = btn.dataset.title || '';
const image = btn.dataset.image || '';
// remove if already selected
if (selected.has(variantId)) {
selected.delete(variantId);
updateUI();
return;
}
// enforce max
if (selected.size >= max) {
setError(`You can only select up to ${max} boards.`);
return;
}
selected.set(variantId, { title, image });
updateUI();
}
// click to pick
pickButtons.forEach(btn => {
btn.addEventListener('click', () => togglePick(btn));
});
// remove X
root.addEventListener('click', (e) => {
const t = e.target;
if (!(t instanceof HTMLElement)) return;
const id = t.getAttribute('data-mm-remove');
if (!id) return;
selected.delete(id);
updateUI();
});
// Add to cart
addBtn.addEventListener('click', async () => {
setError('');
if (selected.size < min) {
setError(`Select at least ${min} boards.`);
return;
}
if (selected.size > max) {
setError(`Select no more than ${max} boards.`);
return;
}
addBtn.disabled = true;
addBtn.classList.add('is-loading');
try {
const items = Array.from(selected.keys()).map(id => ({
id: Number(id),
quantity: 1
}));
const res = await fetch('/cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify({ items })
});
if (!res.ok) {
const txt = await res.text();
throw new Error(txt || 'Add to cart failed');
}
// go to cart
window.location.href = '/cart';
} catch (err) {
console.error(err);
setError('Something went wrong adding to cart. Please try again.');
addBtn.disabled = false;
} finally {
addBtn.classList.remove('is-loading');
}
});
updateUI();
})();
Pick ${min}–${max} boards from the left.
`;
} else {
for (const [variantId, item] of selected.entries()) {
const el = document.createElement('div');
el.className = 'mm-sel';
el.innerHTML = `