Skip to content
Merged
Show file tree
Hide file tree
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
33 changes: 21 additions & 12 deletions reproducibility/site/src/components/InteractiveTable.astro
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,22 @@ const initialSortAttr = initialSort

if (totalCounter) totalCounter.textContent = String(allRows.length);

// Pre-cache sortable values + searchable text per row.
// Cache only the searchable text per row. Sort values are read live
// from the DOM on each setSort() so dynamic updates (e.g. the home
// page's metric toggle rewriting data-sort-value when switching
// between nDCG and recall) take effect immediately.
const meta = allRows.map((tr) => ({
tr,
searchText: tr.textContent?.toLowerCase() ?? "",
cells: Array.from(tr.cells).map((c) => {
const raw = c.dataset.sortValue ?? c.textContent ?? "";
const num = parseFloat(raw);
return { raw, num: Number.isFinite(num) ? num : null };
}),
}));

function cellSortValue(tr: HTMLTableRowElement, colIdx: number): { raw: string; num: number | null } {
const c = tr.cells[colIdx];
const raw = c?.dataset?.sortValue ?? c?.textContent ?? "";
const n = parseFloat(raw);
return { raw, num: Number.isFinite(n) ? n : null };
}

let currentSort: { column: number; direction: "asc" | "desc" } | null = null;

function addArrows() {
Expand All @@ -114,18 +119,22 @@ const initialSortAttr = initialSort
}
});
const sorted = [...meta].sort((a, b) => {
const av = a.cells[colIdx];
const bv = b.cells[colIdx];
if (av?.num !== null && bv?.num !== null) {
return dir === "asc" ? av.num! - bv.num! : bv.num! - av.num!;
const av = cellSortValue(a.tr, colIdx);
const bv = cellSortValue(b.tr, colIdx);
if (av.num !== null && bv.num !== null) {
return dir === "asc" ? av.num - bv.num : bv.num - av.num;
}
return dir === "asc"
? (av?.raw ?? "").localeCompare(bv?.raw ?? "")
: (bv?.raw ?? "").localeCompare(av?.raw ?? "");
? av.raw.localeCompare(bv.raw)
: bv.raw.localeCompare(av.raw);
});
const frag = document.createDocumentFragment();
sorted.forEach((m) => frag.appendChild(m.tr));
tbody.appendChild(frag);
// After moving rows, re-apply search so chip-hidden / search-filtered
// rows stay hidden (display state lives on the inline style of each tr
// but appendChild doesn't preserve it implicitly across all browsers).
applySearch();
}

function applySearch() {
Expand Down
14 changes: 7 additions & 7 deletions reproducibility/site/src/pages/datasets/[id].astro
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ const metricCols: string[] = view?.metric_columns ?? datasetMeta?.eval_metrics ?
) : (
<div class="mt-6">
<InteractiveTable searchPlaceholder="Filter by method, model, retriever…">
<div class="overflow-x-auto">
<div class="max-h-[70vh] overflow-x-auto overflow-y-auto rounded border border-qg-border">
<table class="w-full border-collapse text-sm">
<thead>
<tr class="border-b border-qg-border text-left">
<th class="px-3 py-2">Method</th>
<th class="px-3 py-2">Model</th>
<th class="px-3 py-2">Retriever</th>
<thead class="sticky top-0 z-10 bg-qg-bg-soft shadow-[inset_0_-1px_0_var(--qg-border)]">
<tr class="text-left">
<th class="px-3 py-2 whitespace-nowrap">Method</th>
<th class="px-3 py-2 whitespace-nowrap">Model</th>
<th class="px-3 py-2 whitespace-nowrap">Retriever</th>
{metricCols.map((m) => (
<th class="px-3 py-2 text-right qg-mono text-xs">{m}</th>
<th class="px-3 py-2 text-right qg-mono text-xs whitespace-nowrap">{m}</th>
))}
<th class="px-3 py-2 text-right" data-sort-skip>Run</th>
</tr>
Expand Down
67 changes: 36 additions & 31 deletions reproducibility/site/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -61,25 +61,31 @@ const datasetCols = matrix.dataset_columns;
{
populated && (
<section class="mb-4">
<div class="flex flex-wrap items-center gap-3 text-sm">
<span class="text-qg-fg-muted">Retriever:</span>
<div id="qg-filter-retriever" class="flex flex-wrap gap-1.5">
<button data-value="" class="qg-chip qg-chip-active">All</button>
{retrievers.map((r: any) => (
<button data-value={r.id} class="qg-chip">{r.display_name}</button>
))}
<div class="flex flex-wrap gap-x-6 gap-y-3 text-sm">
<div class="flex flex-wrap items-center gap-2">
<span class="text-qg-fg-muted">Retriever:</span>
<div id="qg-filter-retriever" class="flex flex-wrap gap-1.5">
<button data-value="" class="qg-chip qg-chip-active">All</button>
{retrievers.map((r: any) => (
<button data-value={r.id} class="qg-chip">{r.display_name}</button>
))}
</div>
</div>
<span class="ml-4 text-qg-fg-muted">Model:</span>
<div id="qg-filter-model" class="flex flex-wrap gap-1.5">
<button data-value="" class="qg-chip qg-chip-active">All</button>
{models.map((m: any) => (
<button data-value={m.id} class="qg-chip">{m.display ?? m.id}</button>
))}
<div class="flex flex-wrap items-center gap-2">
<span class="text-qg-fg-muted">Model:</span>
<div id="qg-filter-model" class="flex flex-wrap gap-1.5">
<button data-value="" class="qg-chip qg-chip-active">All</button>
{models.map((m: any) => (
<button data-value={m.id} class="qg-chip">{m.display ?? m.id}</button>
))}
</div>
</div>
<span class="ml-4 text-qg-fg-muted">Metric:</span>
<div id="qg-filter-metric" class="flex flex-wrap gap-1.5">
<button data-value="primary" class="qg-chip qg-chip-active">nDCG@10</button>
<button data-value="secondary" class="qg-chip">Recall</button>
<div class="flex flex-wrap items-center gap-2">
<span class="text-qg-fg-muted">Metric:</span>
<div id="qg-filter-metric" class="flex flex-wrap gap-1.5">
<button data-value="primary" class="qg-chip qg-chip-active">nDCG@10</button>
<button data-value="secondary" class="qg-chip">Recall</button>
</div>
</div>
</div>
</section>
Expand All @@ -89,26 +95,25 @@ const datasetCols = matrix.dataset_columns;
{
populated ? (
<InteractiveTable searchPlaceholder="Filter by method, model, retriever…">
<div class="overflow-x-auto rounded border border-qg-border">
<div class="max-h-[70vh] overflow-x-auto overflow-y-auto rounded border border-qg-border">
<table id="qg-matrix" class="w-full text-sm">
<thead class="bg-qg-bg-soft text-xs uppercase tracking-wide text-qg-fg-muted">
<thead class="sticky top-0 z-10 bg-qg-bg-soft text-xs uppercase tracking-wide text-qg-fg-muted shadow-[inset_0_-1px_0_var(--qg-border)]">
<tr>
<th class="px-3 py-2 text-left">Method</th>
<th class="px-3 py-2 text-left">Model</th>
<th class="px-3 py-2 text-left">Retriever</th>
<th class="px-3 py-2 text-left whitespace-nowrap">Method</th>
<th class="px-3 py-2 text-left whitespace-nowrap">Model</th>
<th class="px-3 py-2 text-left whitespace-nowrap">Retriever</th>
{datasetCols.map((d: any) => (
<th
class="qg-mono px-3 py-2 text-right text-xs"
class="qg-mono px-2 py-2 text-right text-xs whitespace-nowrap"
title={d.id}
>
<span class="qg-col-label-primary">
{SHORT[d.id] ?? d.name}
<span class="text-qg-fg-muted"> / {METRIC_LABEL[d.primary_metric] ?? d.primary_metric}</span>
</span>
<span class="qg-col-label-secondary hidden">
{SHORT[d.id] ?? d.name}
<span class="text-qg-fg-muted"> / {METRIC_LABEL[d.secondary_metric] ?? d.secondary_metric}</span>
</span>
<div>{SHORT[d.id] ?? d.name}</div>
<div class="qg-col-label-primary text-[10px] font-normal text-qg-fg-muted">
{METRIC_LABEL[d.primary_metric] ?? d.primary_metric}
</div>
<div class="qg-col-label-secondary hidden text-[10px] font-normal text-qg-fg-muted">
{METRIC_LABEL[d.secondary_metric] ?? d.secondary_metric}
</div>
</th>
))}
</tr>
Expand Down
Loading