finish peeps tables
This commit is contained in:
+138
-14
@@ -14,6 +14,12 @@
|
||||
section: "",
|
||||
search: "",
|
||||
},
|
||||
sort: {
|
||||
key: "source",
|
||||
dir: "asc",
|
||||
},
|
||||
page: 1,
|
||||
pageSize: 100,
|
||||
};
|
||||
|
||||
const RARE_MODIFIER_VERSIONS = new Set(["v2", "bb"]);
|
||||
@@ -184,6 +190,89 @@
|
||||
fillSelect(qs("#drops-section"), uniqueSorted(rows, "section_id"), "All Section IDs");
|
||||
}
|
||||
|
||||
const SORT_COLUMNS = [
|
||||
["mode", "Mode"],
|
||||
["episode", "Episode"],
|
||||
["difficulty", "Difficulty"],
|
||||
["section_id", "SECID"],
|
||||
["source", "Source"],
|
||||
["item", "Item"],
|
||||
["item_code", "Code"],
|
||||
["rate", "Rate"],
|
||||
];
|
||||
|
||||
const DIFFICULTY_SORT_ORDER = {
|
||||
Normal: 0,
|
||||
Hard: 1,
|
||||
VeryHard: 2,
|
||||
Ultimate: 3,
|
||||
};
|
||||
|
||||
function rateSortValue(rate) {
|
||||
const text = String(rate || "").replaceAll(",", "");
|
||||
const match = text.match(/^(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)$/);
|
||||
if (!match) return Number.NEGATIVE_INFINITY;
|
||||
|
||||
const num = Number(match[1]);
|
||||
const den = Number(match[2]);
|
||||
|
||||
if (!Number.isFinite(num) || !Number.isFinite(den) || den <= 0) {
|
||||
return Number.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
// Sort by actual probability, same as percentage conversion.
|
||||
// 5/8 => 0.625, 1/8192 => 0.000122...
|
||||
return num / den;
|
||||
}
|
||||
|
||||
function sortValue(row, key) {
|
||||
if (key === "difficulty") {
|
||||
return DIFFICULTY_SORT_ORDER[row.difficulty] ?? 999;
|
||||
}
|
||||
|
||||
if (key === "rate") {
|
||||
return rateSortValue(adjustedRate(row.rate));
|
||||
}
|
||||
|
||||
if (key === "item") {
|
||||
return row.item || row.item_code || "";
|
||||
}
|
||||
|
||||
return row[key] || "";
|
||||
}
|
||||
|
||||
function sortedRows(rows) {
|
||||
const { key, dir } = state.sort;
|
||||
const factor = dir === "desc" ? -1 : 1;
|
||||
|
||||
return [...rows].sort((a, b) => {
|
||||
const av = sortValue(a, key);
|
||||
const bv = sortValue(b, key);
|
||||
|
||||
if (typeof av === "number" && typeof bv === "number") {
|
||||
return (av - bv) * factor;
|
||||
}
|
||||
|
||||
return String(av).localeCompare(String(bv), undefined, {
|
||||
numeric: true,
|
||||
sensitivity: "base",
|
||||
}) * factor;
|
||||
});
|
||||
}
|
||||
|
||||
function sortHeader(key, label) {
|
||||
const active = state.sort.key === key;
|
||||
const arrow = active ? (state.sort.dir === "asc" ? "▲" : "▼") : "";
|
||||
const ariaSort = active ? (state.sort.dir === "asc" ? "ascending" : "descending") : "none";
|
||||
|
||||
return `<th aria-sort="${ariaSort}">
|
||||
<button class="drops-sort-button ${active ? "is-active" : ""}" type="button" data-drops-sort="${esc(key)}">
|
||||
<span>${esc(label)}</span>
|
||||
<span class="drops-sort-arrow" aria-hidden="true">${arrow}</span>
|
||||
</button>
|
||||
</th>`;
|
||||
}
|
||||
|
||||
function visibleRows() {
|
||||
const search = state.filters.search.trim().toLowerCase();
|
||||
|
||||
@@ -216,9 +305,14 @@
|
||||
const box = qs("#drops-placeholder");
|
||||
if (!box) return;
|
||||
|
||||
const rows = visibleRows();
|
||||
const rows = sortedRows(visibleRows());
|
||||
const tableLabel = state.table?.label || "Peeps";
|
||||
const shown = rows.slice(0, 1000);
|
||||
const totalPages = Math.max(1, Math.ceil(rows.length / state.pageSize));
|
||||
state.page = Math.min(Math.max(1, state.page), totalPages);
|
||||
|
||||
const start = (state.page - 1) * state.pageSize;
|
||||
const shown = rows.slice(start, start + state.pageSize);
|
||||
const end = start + shown.length;
|
||||
|
||||
const body = shown.map((row) => {
|
||||
const item = row.item || row.item_code || "—";
|
||||
@@ -238,8 +332,8 @@
|
||||
`;
|
||||
}).join("");
|
||||
|
||||
const truncation = rows.length > shown.length
|
||||
? ` Showing first ${shown.length.toLocaleString()}.`
|
||||
const rangeText = rows.length
|
||||
? ` Showing ${Number(start + 1).toLocaleString()}-${Number(end).toLocaleString()}.`
|
||||
: "";
|
||||
const modifier = currentRareModifier();
|
||||
const modifierNote = rareModifierEnabled() && modifier.pct > 0
|
||||
@@ -250,7 +344,7 @@
|
||||
<div class="drops-summary">
|
||||
<div>
|
||||
<strong>Peeps ${esc(tableLabel)} drop table</strong>
|
||||
<span>${rows.length.toLocaleString()} matching rows.${truncation}</span>
|
||||
<span>${rows.length.toLocaleString()} matching rows.${rangeText}</span>
|
||||
${modifierNote}
|
||||
</div>
|
||||
<span>${state.rows.length.toLocaleString()} total rows</span>
|
||||
@@ -259,19 +353,17 @@
|
||||
<table class="drops-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mode</th>
|
||||
<th>Episode</th>
|
||||
<th>Difficulty</th>
|
||||
<th>Section ID</th>
|
||||
<th>Source</th>
|
||||
<th>Item</th>
|
||||
<th>Item Code</th>
|
||||
<th>Rate</th>
|
||||
${SORT_COLUMNS.map(([key, label]) => sortHeader(key, label)).join("")}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>${body || `<tr><td colspan="8">No drops match these filters.</td></tr>`}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="leaderboard-pager drops-pager">
|
||||
<button type="button" data-drops-page="prev" ${state.page <= 1 ? "disabled" : ""}>Previous</button>
|
||||
<span>Page ${state.page} of ${totalPages}</span>
|
||||
<button type="button" data-drops-page="next" ${state.page >= totalPages ? "disabled" : ""}>Next</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -299,6 +391,7 @@
|
||||
state.filters.difficulty = "";
|
||||
state.filters.section = "";
|
||||
state.filters.search = "";
|
||||
state.page = 1;
|
||||
|
||||
if (qs("#drops-search")) qs("#drops-search").value = "";
|
||||
|
||||
@@ -335,30 +428,61 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
qs("#drops-mode")?.addEventListener("change", updateMode);
|
||||
qs("#drops-version")?.addEventListener("change", loadPeeps);
|
||||
qs("#drops-rare-modifier")?.addEventListener("change", renderTable);
|
||||
qs("#drops-rare-modifier")?.addEventListener("change", () => {
|
||||
state.page = 1;
|
||||
state.page = 1;
|
||||
renderTable();
|
||||
});
|
||||
|
||||
qs("#drops-rare-mode")?.addEventListener("change", (event) => {
|
||||
state.filters.mode = event.target.value;
|
||||
state.page = 1;
|
||||
renderTable();
|
||||
});
|
||||
|
||||
qs("#drops-episode")?.addEventListener("change", (event) => {
|
||||
state.filters.episode = event.target.value;
|
||||
state.page = 1;
|
||||
renderTable();
|
||||
});
|
||||
|
||||
qs("#drops-difficulty")?.addEventListener("change", (event) => {
|
||||
state.filters.difficulty = event.target.value;
|
||||
state.page = 1;
|
||||
renderTable();
|
||||
});
|
||||
|
||||
qs("#drops-section")?.addEventListener("change", (event) => {
|
||||
state.filters.section = event.target.value;
|
||||
state.page = 1;
|
||||
renderTable();
|
||||
});
|
||||
|
||||
qs("#drops-search")?.addEventListener("input", (event) => {
|
||||
state.filters.search = event.target.value;
|
||||
state.page = 1;
|
||||
renderTable();
|
||||
});
|
||||
|
||||
qs("#drops-placeholder")?.addEventListener("click", (event) => {
|
||||
const pageButton = event.target.closest("[data-drops-page]");
|
||||
if (pageButton) {
|
||||
state.page += pageButton.dataset.dropsPage === "next" ? 1 : -1;
|
||||
renderTable();
|
||||
return;
|
||||
}
|
||||
|
||||
const button = event.target.closest("[data-drops-sort]");
|
||||
if (!button) return;
|
||||
|
||||
const key = button.dataset.dropsSort;
|
||||
if (state.sort.key === key) {
|
||||
state.sort.dir = state.sort.dir === "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
state.sort.key = key;
|
||||
state.sort.dir = "asc";
|
||||
}
|
||||
|
||||
renderTable();
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -126,6 +126,6 @@
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="drop-tables.js?v=drops-peeps-table-viewer-20260613-6" defer></script>
|
||||
<script src="drop-tables.js?v=drops-peeps-table-viewer-20260613-11" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2839,3 +2839,37 @@ button.inline-link,
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.drops-sort-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.4rem;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
letter-spacing: inherit;
|
||||
text-align: left;
|
||||
text-transform: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.drops-sort-button:hover,
|
||||
.drops-sort-button.is-active {
|
||||
color: rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
.drops-sort-arrow {
|
||||
min-width: 0.75rem;
|
||||
color: rgba(255, 255, 255, 0.62);
|
||||
font-size: 0.66rem;
|
||||
}
|
||||
|
||||
|
||||
.drops-pager {
|
||||
margin: 0.9rem 1rem 1rem;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user