← 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 (_) {}
});
});
})();