(() => { 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 = `
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 = ` ${escapeHtml(item.title)} `; 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(); })();