← back to AbramsOS

public/js/sort-density.js

110 lines

// Canonical sort + density slider for AbramsOS grids.
// Inherits the pattern from /sort-skill (corkwallcovering, dw-collections-viewer).
// Persists choices in localStorage so they survive reloads.

// ── Legacy: the original purchases grid (kept verbatim for back-compat) ──────
(function () {
  const grid = document.getElementById('purchaseGrid');
  if (!grid) return;

  const sortSelect = document.getElementById('sortSelect');
  const densityRange = document.getElementById('densityRange');

  const SORT_KEY = 'abramsos.sort.purchase';
  const DENSITY_KEY = 'abramsos.density.purchase';

  function applyDensity(px) {
    grid.style.gridTemplateColumns = `repeat(auto-fill, minmax(${px}px, 1fr))`;
  }

  function applySort(mode) {
    const cards = Array.from(grid.children);
    const cmp = {
      'date-desc': (a, b) => new Date(b.dataset.date) - new Date(a.dataset.date),
      'date-asc': (a, b) => new Date(a.dataset.date) - new Date(b.dataset.date),
      'merchant': (a, b) => a.dataset.merchant.localeCompare(b.dataset.merchant),
      'amount-desc': (a, b) => Number(b.dataset.amount) - Number(a.dataset.amount),
      'amount-asc': (a, b) => Number(a.dataset.amount) - Number(b.dataset.amount),
    }[mode] || (() => 0);
    cards.sort(cmp).forEach((c) => grid.appendChild(c));
  }

  try {
    const savedSort = localStorage.getItem(SORT_KEY);
    if (savedSort && sortSelect) { sortSelect.value = savedSort; applySort(savedSort); }
    const savedDensity = localStorage.getItem(DENSITY_KEY);
    if (savedDensity && densityRange) { densityRange.value = savedDensity; applyDensity(Number(savedDensity)); }
  } catch (e) {}

  sortSelect?.addEventListener('change', (e) => {
    applySort(e.target.value);
    try { localStorage.setItem(SORT_KEY, e.target.value); } catch (_) {}
  });
  densityRange?.addEventListener('input', (e) => {
    applyDensity(Number(e.target.value));
    try { localStorage.setItem(DENSITY_KEY, e.target.value); } catch (_) {}
  });
})();

// ── Generic: any element with [data-grid="<key>"] ───────────────────────────
// Controls matched by <select data-sort-for="<key>"> and
// <input type="range" data-density-for="<key>">. Cards carry data-<field>
// attributes; option values are "<field>" (asc) or "<field>-asc"/"<field>-desc".
// Field type (number / date / string) is auto-detected from the value.
(function () {
  document.querySelectorAll('[data-grid]').forEach((grid) => {
    const key = grid.getAttribute('data-grid');
    const sortSelect = document.querySelector(`[data-sort-for="${key}"]`);
    const densityRange = document.querySelector(`[data-density-for="${key}"]`);
    const SORT_KEY = `abramsos.sort.${key}`;
    const DENSITY_KEY = `abramsos.density.${key}`;

    function detect(v) {
      if (v === undefined || v === null || v === '') return { t: 'empty', v: null };
      if (!isNaN(Number(v))) return { t: 'num', v: Number(v) };
      const d = Date.parse(v);
      if (!isNaN(d)) return { t: 'date', v: d };
      return { t: 'str', v: String(v).toLowerCase() };
    }

    function applyDensity(px) {
      grid.style.gridTemplateColumns = `repeat(auto-fill, minmax(${px}px, 1fr))`;
    }

    function applySort(mode) {
      if (!mode) return;
      let field = mode, dir = 'asc';
      const m = mode.match(/^(.*)-(asc|desc)$/);
      if (m) { field = m[1]; dir = m[2]; }
      const sign = dir === 'desc' ? -1 : 1;
      Array.from(grid.children).sort((a, b) => {
        const av = detect(a.dataset[field]);
        const bv = detect(b.dataset[field]);
        if (av.t === 'empty' && bv.t === 'empty') return 0;
        if (av.t === 'empty') return 1;   // empties always last
        if (bv.t === 'empty') return -1;
        if (av.v < bv.v) return -1 * sign;
        if (av.v > bv.v) return 1 * sign;
        return 0;
      }).forEach((c) => grid.appendChild(c));
    }

    try {
      const savedSort = localStorage.getItem(SORT_KEY);
      if (savedSort && sortSelect) sortSelect.value = savedSort;
      applySort(sortSelect ? sortSelect.value : null);
      const savedDensity = localStorage.getItem(DENSITY_KEY);
      if (savedDensity && densityRange) { densityRange.value = savedDensity; applyDensity(Number(savedDensity)); }
    } catch (e) {}

    sortSelect?.addEventListener('change', (e) => {
      applySort(e.target.value);
      try { localStorage.setItem(SORT_KEY, e.target.value); } catch (_) {}
    });
    densityRange?.addEventListener('input', (e) => {
      applyDensity(Number(e.target.value));
      try { localStorage.setItem(DENSITY_KEY, e.target.value); } catch (_) {}
    });
  });
})();