diff --git a/src/components/Table.astro b/src/components/Table.astro
index 502a8db..b423151 100644
--- a/src/components/Table.astro
+++ b/src/components/Table.astro
@@ -37,6 +37,8 @@ const id = `table-${Math.random().toString(36).slice(2, 9)}`;
)
}
+
{
+ e.stopPropagation();
+ removeBadge(key);
+ });
+ }
+ }
+
+ function badgeTemplate(key, order) {
+ // returns HTML string for a badge; includes data-key and close button
+ return `
+
+ ${escapeHtml(key)}: ${escapeHtml(order)}
+
+
+ `;
+ }
+
+ function removeBadge(key) {
+ // remove from DOM
+ const el = badgeContainer.querySelector(
+ `[data-badge-key="${key}"]`,
+ );
+ if (el) el.remove();
+
+ // remove from sort map
+ if (sort.hasOwnProperty(key)) {
+ delete sort[key];
+ }
+
+ // update header indicator (if any)
+ const th = Array.from(sortHeaders).find(
+ (t) => t.dataset.sortKey === key,
+ );
+ if (th) {
+ const indicator = th.querySelector(".sort-indicator");
+ if (indicator) {
+ indicator.setAttribute(
+ "data-icon",
+ "solar:sort-vertical-line-duotone",
+ );
+ }
+ }
+
+ // reload data
+ currentPage = 1;
+ loadData();
+ }
+
+ function escapeHtml(str) {
+ return String(str)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
+
+ // ---------- ROW rendering (unchanged) ----------
function renderRows(dataset) {
tbody.innerHTML = "";
mobileBody.innerHTML = "";
@@ -343,26 +428,23 @@ const id = `table-${Math.random().toString(36).slice(2, 9)}`;
}
// sort locally if needed
- if (Object.keys(sort).length !== 0) {
- for (const key in sort) {
- const colKey = key;
- const colSortOrder = sort[key];
- if (colKey) {
- currentData.sort((a, b) => {
- const aVal = String(a[colKey] ?? "");
- const bVal = String(b[colKey] ?? "");
- if (aVal < bVal)
- return colSortOrder === "asc" ? -1 : 1;
- if (aVal > bVal)
- return colSortOrder === "asc" ? 1 : -1;
- return 0;
- });
- }
- }
+ const sortKeys = Object.keys(sort);
+ if (sortKeys.length !== 0) {
+ // stable multi-sort — apply keys in reverse so the first sort key has priority
+ sortKeys.reverse().forEach((k) => {
+ const colKey = k;
+ const colSortOrder = sort[k];
+ currentData.sort((a, b) => {
+ const aVal = String(a[colKey] ?? "");
+ const bVal = String(b[colKey] ?? "");
+ if (aVal < bVal) return colSortOrder === "asc" ? -1 : 1;
+ if (aVal > bVal) return colSortOrder === "asc" ? 1 : -1;
+ return 0;
+ });
+ });
}
// paginate
-
renderRows(paginated);
renderPagination(totalRows);
}
@@ -432,10 +514,12 @@ const id = `table-${Math.random().toString(36).slice(2, 9)}`;
});
// 🧭 Sorting
- sortHeaders.forEach((th, index) => {
+ sortHeaders.forEach((th) => {
th.addEventListener("click", () => {
const key = th.dataset.sortKey;
+ if (!key) return;
+ // compute new sort order
let sortOrder = "asc";
if (sort.hasOwnProperty(key)) {
sortOrder = sort[key] === "asc" ? "desc" : "asc";
@@ -444,13 +528,30 @@ const id = `table-${Math.random().toString(36).slice(2, 9)}`;
sort[key] = sortOrder;
}
- th.querySelector(".sort-indicator")?.setAttribute(
- "data-icon",
- sortOrder === "asc"
- ? "solar:sort-up-bold-duotone"
- : "solar:sort-down-bold-duotone",
- );
+ // update header icon (single header)
+ sortHeaders.forEach((h) => {
+ const ind = h.querySelector(".sort-indicator");
+ if (!ind) return;
+ if (h.dataset.sortKey === key) {
+ ind.setAttribute(
+ "data-icon",
+ sortOrder === "asc"
+ ? "solar:sort-up-bold-duotone"
+ : "solar:sort-down-bold-duotone",
+ );
+ } else {
+ ind.setAttribute(
+ "data-icon",
+ "solar:sort-vertical-line-duotone",
+ );
+ }
+ });
+ // create / update badge
+ createOrUpdateBadge(key, sort[key]);
+
+ // reload data
+ currentPage = 1;
loadData();
});
});