Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions js/asset-portal-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,61 @@

const totalCount = items.length;
const defaultTitle = catalog.dataset.defaultTitle || "All assets";
let selectedTag = normalizeTag(catalog.dataset.initialTag) || "all";
let sortOrder = normalize(catalog.dataset.initialSort) === "timestamp" ? "timestamp" : "stars";
const initialTag = normalizeTag(catalog.dataset.initialTag) || "all";
const initialSort = normalize(catalog.dataset.initialSort) === "timestamp" ? "timestamp" : "stars";
let selectedTag = initialTag;
let sortOrder = initialSort;

if (!findTagButton(tagButtons, selectedTag)) {
selectedTag = "all";
}

const readUrlState = () => {
const params = new URLSearchParams(window.location.search);
const urlTag = params.get("tag");
const urlSort = params.get("sort");

selectedTag = urlTag === null ? initialTag : normalizeTag(urlTag) || "all";
if (!findTagButton(tagButtons, selectedTag)) {
selectedTag = "all";
}

sortOrder = urlSort === null
? initialSort
: urlSort === "timestamp" ? "timestamp" : "stars";

if (searchInput) {
searchInput.value = params.get("q") || "";
}
};

const syncUrl = () => {
const params = new URLSearchParams(window.location.search);
const query = (searchInput?.value || "").trim();

if (query) {
params.set("q", query);
} else {
params.delete("q");
}

if (selectedTag !== initialTag) {
params.set("tag", selectedTag);
} else {
params.delete("tag");
}

if (sortOrder !== initialSort) {
params.set("sort", sortOrder);
} else {
params.delete("sort");
Comment thread
AGulev marked this conversation as resolved.
}

const search = params.toString();
const nextUrl = `${window.location.pathname}${search ? `?${search}` : ""}${window.location.hash}`;
window.history.replaceState(window.history.state, "", nextUrl);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only concern is a frequent call to replaceState at every input, but I'm not sure if this will be much of an issue in production, compared to the benefits of the PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a valid question. I don’t think this should be an issue. replaceState() is relatively lightweight and, unlike pushState(), doesn’t create a new history entry on every input. It only updates the current URL without triggering navigation or reloading the page.

};

const sortItems = () => {
const ordered = [...items].sort((a, b) => {
const metricA = Number.parseFloat(a.dataset[`asset${sortOrder === "timestamp" ? "Timestamp" : "Stars"}`] || "0");
Expand Down Expand Up @@ -191,6 +239,7 @@
button.addEventListener("click", () => {
selectedTag = normalizeTag(button.dataset.assetTag) || "all";
applyState();
syncUrl();
trackEvent("asset_portal_tag_filter_select", {
tag: selectedTag,
sort_order: sortOrder
Expand All @@ -202,6 +251,7 @@
button.addEventListener("click", () => {
sortOrder = button.dataset.assetSort === "timestamp" ? "timestamp" : "stars";
applyState();
syncUrl();
trackEvent("asset_portal_sort_select", {
tag: selectedTag,
sort_order: sortOrder
Expand All @@ -210,11 +260,20 @@
});

if (searchInput) {
const onInput = () => applyState();
const onInput = () => {
applyState();
syncUrl();
};
searchInput.addEventListener("input", onInput);
searchInput.addEventListener("propertychange", onInput);
}

window.addEventListener("popstate", () => {
readUrlState();
applyState();
});

readUrlState();
applyState();
});
});
Expand Down