[object Object]

← back to Glassbeadedwallpaper

glass: make sort+density controls functional

c1d4d44463dab7447e61ea9aaf5a785dbeaa09d2 · 2026-05-27 16:34:56 -0700 · Steve

Two bugs: (1) controls-wiring IIFE ran before #productsGrid was declared so
the density slider + sort listener never bound; (2) even if bound, the sort
change handler was a /* TODO: re-sort live */ no-op. Fixes: added a generic
sortProducts(list,mode) (Newest/Color/Style/SKU/Title) + window.applySort()
that re-renders from the top, deferred the wiring to DOMContentLoaded, and
made loadProducts honor the saved sort (default Newest) instead of always
sorting by color on load. Both controls remain localStorage-persisted.

Files touched

Diff

commit c1d4d44463dab7447e61ea9aaf5a785dbeaa09d2
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed May 27 16:34:56 2026 -0700

    glass: make sort+density controls functional
    
    Two bugs: (1) controls-wiring IIFE ran before #productsGrid was declared so
    the density slider + sort listener never bound; (2) even if bound, the sort
    change handler was a /* TODO: re-sort live */ no-op. Fixes: added a generic
    sortProducts(list,mode) (Newest/Color/Style/SKU/Title) + window.applySort()
    that re-renders from the top, deferred the wiring to DOMContentLoaded, and
    made loadProducts honor the saved sort (default Newest) instead of always
    sorting by color on load. Both controls remain localStorage-persisted.
---
 index.html | 92 ++++++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 65 insertions(+), 27 deletions(-)

diff --git a/index.html b/index.html
index ec1ddb8..e9be356 100644
--- a/index.html
+++ b/index.html
@@ -370,33 +370,44 @@
   <input id="density" type="range" min="180" max="380" step="10" value="240" style="accent-color:var(--accent,#A98B5B);width:140px;cursor:pointer">
 </div>
 <script>
+// Controls wiring — deferred to DOMContentLoaded so #productsGrid (declared after
+// this block) exists; otherwise getElementById returns null and the density slider
+// + sort listener never bind (controls render but stay inert).
 (function(){
-  const grid = document.getElementById('productGrid') || document.getElementById('productsGrid');
-  const dens = document.getElementById('density');
-  const sortSel = document.getElementById('sort');
-  const search = document.getElementById('grid-search');
-  if (!grid) return;
-  const STORE = 'glassbeadedwallpaper_grid';
-  const saved = (() => { try { return JSON.parse(localStorage.getItem(STORE) || '{}'); } catch { return {}; } })();
-  function applyDensity(v){ grid.style.gridTemplateColumns = 'repeat(auto-fill,minmax(' + v + 'px,1fr))'; }
-  if (dens) {
-    dens.value = saved.density || 240;
-    applyDensity(dens.value);
-    dens.addEventListener('input', () => { applyDensity(dens.value); saved.density = dens.value; localStorage.setItem(STORE, JSON.stringify(saved)); });
-  }
-  if (sortSel) {
-    sortSel.value = saved.sort || 'newest';
-    sortSel.addEventListener('change', () => { saved.sort = sortSel.value; localStorage.setItem(STORE, JSON.stringify(saved)); /* TODO: re-sort live */ });
-  }
-  if (search) {
-    search.addEventListener('input', () => {
-      const q = search.value.toLowerCase().trim();
-      Array.from(grid.children).forEach(card => {
-        const t = (card.textContent || '').toLowerCase();
-        card.style.display = !q || t.includes(q) ? '' : 'none';
+  function wire(){
+    const grid = document.getElementById('productGrid') || document.getElementById('productsGrid');
+    const dens = document.getElementById('density');
+    const sortSel = document.getElementById('sort');
+    const search = document.getElementById('grid-search');
+    if (!grid) return;
+    const STORE = 'glassbeadedwallpaper_grid';
+    const saved = (() => { try { return JSON.parse(localStorage.getItem(STORE) || '{}'); } catch { return {}; } })();
+    function applyDensity(v){ grid.style.gridTemplateColumns = 'repeat(auto-fill,minmax(' + v + 'px,1fr))'; }
+    if (dens) {
+      dens.value = saved.density || 240;
+      applyDensity(dens.value);
+      dens.addEventListener('input', () => { applyDensity(dens.value); saved.density = dens.value; localStorage.setItem(STORE, JSON.stringify(saved)); });
+    }
+    if (sortSel) {
+      sortSel.value = saved.sort || 'newest';
+      sortSel.addEventListener('change', () => {
+        saved.sort = sortSel.value;
+        localStorage.setItem(STORE, JSON.stringify(saved));
+        if (typeof window.applySort === 'function') window.applySort(sortSel.value);
       });
-    });
+    }
+    if (search) {
+      search.addEventListener('input', () => {
+        const q = search.value.toLowerCase().trim();
+        Array.from(grid.children).forEach(card => {
+          const t = (card.textContent || '').toLowerCase();
+          card.style.display = !q || t.includes(q) ? '' : 'none';
+        });
+      });
+    }
   }
+  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', wire);
+  else wire();
 })();
 </script>
 
@@ -538,6 +549,31 @@
             });
         }
 
+        // Generic multi-mode sort (Newest/Color/Style/SKU/Title) for the sort <select>.
+        function sortProducts(list, mode) {
+            const arr = [...list];
+            const sku = p => (p.primarySku || p.handle || '').toLowerCase();
+            const title = p => (p.title || '').toLowerCase();
+            const style = p => ((p.tags && p.tags[0]) || p.category || p.productType || '~').toLowerCase();
+            switch (mode) {
+                case 'color': return sortProductsByColor(arr);
+                case 'style': arr.sort((a, b) => style(a).localeCompare(style(b)) || title(a).localeCompare(title(b))); break;
+                case 'sku': arr.sort((a, b) => sku(a).localeCompare(sku(b))); break;
+                case 'title': arr.sort((a, b) => title(a).localeCompare(title(b))); break;
+                case 'newest': default: arr.sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0)); break;
+            }
+            return arr;
+        }
+
+        // Re-sort the current (possibly color-filtered) set and re-render from the top.
+        window.applySort = function (mode) {
+            filteredProducts = sortProducts(filteredProducts, mode);
+            displayedProducts = 0;
+            const grid = document.getElementById('productsGrid');
+            if (grid) grid.innerHTML = '';
+            displayBatch();
+        };
+
         async function loadProducts() {
             const grid = document.getElementById('productsGrid');
             console.log('Loading glass beaded products...');
@@ -557,11 +593,13 @@
 
                 console.log(`✓ Loaded ${allProducts.length} glass beaded products`);
 
-                // Sort products by color on load
+                // Apply saved sort on load (default Newest per hard-rule), not always-color.
                 try {
-                    allProducts = sortProductsByColor(allProducts);
+                    let savedSort = 'newest';
+                    try { savedSort = (JSON.parse(localStorage.getItem('glassbeadedwallpaper_grid') || '{}').sort) || 'newest'; } catch {}
+                    allProducts = sortProducts(allProducts, savedSort);
                     filteredProducts = allProducts;
-                    console.log('Products sorted by color');
+                    console.log('Products sorted by', savedSort);
                 } catch (err) {
                     console.error('Error sorting products:', err);
                 }

← 19eb646 redact vendor names from customer-facing /api (close fleet l  ·  back to Glassbeadedwallpaper  ·  gitignore: add backup/orig/old file patterns to prevent sour a5f7d52 →