-
Max price:
-
-
+
@@ -253,7 +279,7 @@ export async function renderStore($app, storeLabelRaw) {
return (String(a.name) + a.sku).localeCompare(String(b.name) + b.sku);
});
- // ---- Max price slider (exponential mapping) ----
+ // ---- Max price slider (exponential mapping + clicky rounding) ----
const MIN_PRICE = 25;
function maxStorePriceOnPage() {
@@ -267,11 +293,20 @@ export async function renderStore($app, storeLabelRaw) {
}
const pageMax = maxStorePriceOnPage();
- // If nothing priced, hide slider (still functional, but not meaningful)
const boundMax = pageMax !== null ? Math.max(MIN_PRICE, pageMax) : MIN_PRICE;
- // Exponential scale: t in [0..1] maps price in [MIN_PRICE..boundMax]
- // price = MIN_PRICE * exp( ln(boundMax/MIN_PRICE) * t )
+ function stepForPrice(p) {
+ const x = Number.isFinite(p) ? p : boundMax;
+ if (x < 120) return 5;
+ if (x < 250) return 10;
+ if (x < 600) return 25;
+ return 100;
+ }
+ function roundToStep(p) {
+ const step = stepForPrice(p);
+ return Math.round(p / step) * step;
+ }
+
function priceFromT(t) {
t = Math.max(0, Math.min(1, t));
if (boundMax <= MIN_PRICE) return MIN_PRICE;
@@ -291,9 +326,18 @@ export async function renderStore($app, storeLabelRaw) {
return Math.max(MIN_PRICE, Math.min(boundMax, p));
}
- // Initialize selected max price:
- // default = highest price on page, otherwise MIN_PRICE
- let selectedMaxPrice = clampPrice(
+ function clampAndRound(p) {
+ const c = clampPrice(p);
+ const r = roundToStep(c);
+ return clampPrice(r);
+ }
+
+ function formatDollars(p) {
+ if (!Number.isFinite(p)) return "";
+ return `$${Math.round(p)}`;
+ }
+
+ let selectedMaxPrice = clampAndRound(
savedMaxPrice !== null ? savedMaxPrice : boundMax
);
@@ -302,18 +346,13 @@ export async function renderStore($app, storeLabelRaw) {
const v = Math.round(t * 1000);
$maxPrice.value = String(v);
}
- function getPriceFromSlider() {
+
+ function getRawPriceFromSlider() {
const v = Number($maxPrice.value);
const t = Number.isFinite(v) ? v / 1000 : 1;
return clampPrice(priceFromT(t));
}
- function formatDollars(p) {
- if (!Number.isFinite(p)) return "";
- const rounded = Math.round(p);
- return `$${rounded}`;
- }
-
function updateMaxPriceLabel() {
if (pageMax === null) {
$maxPriceLabel.textContent = "No prices";
@@ -326,21 +365,27 @@ export async function renderStore($app, storeLabelRaw) {
}
if (pageMax === null) {
- // No prices found; slider isn't useful.
$maxPrice.disabled = true;
$priceWrap.title = "No priced items in this store.";
- setSliderFromPrice(boundMax);
selectedMaxPrice = boundMax;
+ setSliderFromPrice(boundMax);
localStorage.setItem(LS_MAX_PRICE, String(selectedMaxPrice));
updateMaxPriceLabel();
} else {
- // Clamp saved value to bounds (and write back clamped value)
- selectedMaxPrice = clampPrice(selectedMaxPrice);
+ selectedMaxPrice = clampAndRound(selectedMaxPrice);
localStorage.setItem(LS_MAX_PRICE, String(selectedMaxPrice));
setSliderFromPrice(selectedMaxPrice);
updateMaxPriceLabel();
}
+ // ---- Round listing display price to nearest $1 ----
+ function roundedListingPriceStr(it) {
+ const p = it && Number.isFinite(it._storePrice) ? it._storePrice : null;
+ if (p === null) return it.cheapestPriceStr ? it.cheapestPriceStr : "(no price)";
+ const dollars = Math.round(p);
+ return `$${dollars}`;
+ }
+
function priceBadgeHtml(it) {
if (it._exclusive || it._lastStock) return "";
@@ -360,7 +405,7 @@ export async function renderStore($app, storeLabelRaw) {
}
function renderCard(it) {
- const price = it.cheapestPriceStr ? it.cheapestPriceStr : "(no price)";
+ const price = roundedListingPriceStr(it);
const href = String(it.sampleUrl || "").trim();
const specialBadge = it._lastStock
@@ -406,7 +451,7 @@ export async function renderStore($app, storeLabelRaw) {
}
// ---- Infinite scroll paging (shared across both columns) ----
- const PAGE_SIZE = 140; // total per "page" across both columns
+ const PAGE_SIZE = 140;
const PAGE_EACH = Math.max(1, Math.floor(PAGE_SIZE / 2));
let filteredExclusive = [];
@@ -452,8 +497,16 @@ export async function renderStore($app, storeLabelRaw) {
shownExclusive += sliceEx.length;
shownCompare += sliceCo.length;
- if (sliceEx.length) $resultsExclusive.insertAdjacentHTML("beforeend", sliceEx.map(renderCard).join(""));
- if (sliceCo.length) $resultsCompare.insertAdjacentHTML("beforeend", sliceCo.map(renderCard).join(""));
+ if (sliceEx.length)
+ $resultsExclusive.insertAdjacentHTML(
+ "beforeend",
+ sliceEx.map(renderCard).join("")
+ );
+ if (sliceCo.length)
+ $resultsCompare.insertAdjacentHTML(
+ "beforeend",
+ sliceCo.map(renderCard).join("")
+ );
const total = totalFiltered();
const shown = totalShown();
@@ -467,7 +520,6 @@ export async function renderStore($app, storeLabelRaw) {
}
}
- // Click -> item page (delegated). SKU + Open links stopPropagation already.
$resultsWrap.addEventListener("click", (e) => {
const el = e.target.closest(".item");
if (!el) return;
@@ -485,12 +537,10 @@ export async function renderStore($app, storeLabelRaw) {
let base = items;
- // Search filter
if (tokens.length) {
base = base.filter((it) => matchesAllTokens(it.searchText, tokens));
}
- // Max price filter (include items with no price)
if (pageMax !== null && Number.isFinite(selectedMaxPrice)) {
const cap = selectedMaxPrice + 0.0001;
base = base.filter((it) => {
@@ -506,7 +556,6 @@ export async function renderStore($app, storeLabelRaw) {
renderNext(true);
}
- // Initial render (apply saved query/max price if present)
applyFilter();
const io = new IntersectionObserver(
@@ -527,14 +576,31 @@ export async function renderStore($app, storeLabelRaw) {
});
let tp = null;
+ function setSelectedMaxPriceFromSlider() {
+ const raw = getRawPriceFromSlider();
+ const rounded = clampAndRound(raw);
+ if (Math.abs(rounded - selectedMaxPrice) > 0.001) {
+ selectedMaxPrice = rounded;
+ localStorage.setItem(LS_MAX_PRICE, String(selectedMaxPrice));
+ updateMaxPriceLabel();
+ } else {
+ updateMaxPriceLabel();
+ }
+ }
+
$maxPrice.addEventListener("input", () => {
if (pageMax === null) return;
- selectedMaxPrice = getPriceFromSlider();
- selectedMaxPrice = clampPrice(selectedMaxPrice);
- localStorage.setItem(LS_MAX_PRICE, String(selectedMaxPrice));
- updateMaxPriceLabel();
+ setSelectedMaxPriceFromSlider();
if (tp) clearTimeout(tp);
tp = setTimeout(applyFilter, 40);
});
+
+ $maxPrice.addEventListener("change", () => {
+ if (pageMax === null) return;
+ setSelectedMaxPriceFromSlider();
+ setSliderFromPrice(selectedMaxPrice);
+ updateMaxPriceLabel();
+ applyFilter();
+ });
}