ux: additive filter (click=narrow, more=add) + hub-link icons on every dataset row
Browse files
app.py
CHANGED
|
@@ -261,6 +261,8 @@ td.num{text-align:right;color:var(--muted);font-variant-numeric:tabular-nums;wid
|
|
| 261 |
a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--rule);cursor:pointer}
|
| 262 |
a:hover{border-color:var(--ink)}
|
| 263 |
.meta a{border:0;color:var(--accent)}
|
|
|
|
|
|
|
| 264 |
code{font-family:ui-monospace,monospace;font-size:12.5px;background:#f1f1e6;padding:1px 5px}
|
| 265 |
.placeholder{color:var(--muted);font-style:italic}
|
| 266 |
</style></head><body>
|
|
@@ -300,12 +302,13 @@ const LEGEND_TYPES=['exact_copy','filtered_subset','subset_modified','subset_ref
|
|
| 300 |
'partial_overlap','combined','declared'];
|
| 301 |
const ACTIVE_TYPES=new Set(LEGEND_TYPES);
|
| 302 |
let currentNetwork=null;
|
| 303 |
-
(function(){let h='<div style="font-style:italic;color:var(--muted);margin-bottom:3px">relationship · click to
|
| 304 |
for(const t of LEGEND_TYPES)
|
| 305 |
h+='<div class="lt" data-type="'+t+'"><span class="dot" style="background:'+(COLORS[t]||'#999')+'"></span>'+
|
| 306 |
t.replace(/_/g,' ')+(t==='declared'?' (dashed)':'')+'</div>';
|
| 307 |
-
h+='<div class="reset" data-reset="1">
|
| 308 |
document.getElementById('legend').innerHTML=h;})();
|
|
|
|
| 309 |
function applyFilter(){
|
| 310 |
if(!currentNetwork)return;
|
| 311 |
const edgesDS=currentNetwork.body.data.edges, nodesDS=currentNetwork.body.data.nodes;
|
|
@@ -320,12 +323,28 @@ document.getElementById('legend').addEventListener('click',ev=>{
|
|
| 320 |
const reset=ev.target.closest('[data-reset]');
|
| 321 |
if(reset){LEGEND_TYPES.forEach(t=>ACTIVE_TYPES.add(t));
|
| 322 |
document.querySelectorAll('#legend .lt').forEach(el=>el.classList.remove('off'));
|
| 323 |
-
applyFilter();return;}
|
| 324 |
const lt=ev.target.closest('.lt');
|
| 325 |
if(!lt||!lt.dataset.type)return;
|
| 326 |
const t=lt.dataset.type;
|
| 327 |
-
if(
|
| 328 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
applyFilter();
|
| 330 |
});
|
| 331 |
|
|
@@ -333,8 +352,15 @@ document.getElementById('legend').addEventListener('click',ev=>{
|
|
| 333 |
function bars(rows,opts){
|
| 334 |
opts=opts||{};const max=Math.max(...rows.map(r=>r.n),1);let h='<table class="bars">';
|
| 335 |
for(const r of rows){const w=Math.round(100*r.n/max);
|
| 336 |
-
|
| 337 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
const col=opts.type?(COLORS[r.t]||'#999'):'var(--bar)';
|
| 339 |
h+='<tr><td class="lbl">'+lbl+'</td><td class="bar"><span style="width:'+w+'%;background:'+col+'"></span></td>'
|
| 340 |
+'<td class="num">'+r.n.toLocaleString()+'</td></tr>';}
|
|
@@ -428,6 +454,7 @@ function lst(title,arr){
|
|
| 428 |
let h='<h3>'+title+' · '+arr.length+'</h3>';
|
| 429 |
for(const x of arr){const t=x.primary_type||'';
|
| 430 |
h+='<div class="row">'+dot(t)+'<a data-ds="'+x.id+'">'+x.id+'</a>'+
|
|
|
|
| 431 |
(t?'<span class="ty">'+t.replace(/_/g,' ')+(x.size_ratio?' ×'+x.size_ratio:'')+'</span>':'')+'</div>';}
|
| 432 |
return h;
|
| 433 |
}
|
|
|
|
| 261 |
a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--rule);cursor:pointer}
|
| 262 |
a:hover{border-color:var(--ink)}
|
| 263 |
.meta a{border:0;color:var(--accent)}
|
| 264 |
+
a.hub{border:0;color:var(--accent);font-size:11px;margin-left:4px;padding:0 3px;opacity:.7}
|
| 265 |
+
a.hub:hover{opacity:1}
|
| 266 |
code{font-family:ui-monospace,monospace;font-size:12.5px;background:#f1f1e6;padding:1px 5px}
|
| 267 |
.placeholder{color:var(--muted);font-style:italic}
|
| 268 |
</style></head><body>
|
|
|
|
| 302 |
'partial_overlap','combined','declared'];
|
| 303 |
const ACTIVE_TYPES=new Set(LEGEND_TYPES);
|
| 304 |
let currentNetwork=null;
|
| 305 |
+
(function(){let h='<div style="font-style:italic;color:var(--muted);margin-bottom:3px">relationship · click to narrow</div>';
|
| 306 |
for(const t of LEGEND_TYPES)
|
| 307 |
h+='<div class="lt" data-type="'+t+'"><span class="dot" style="background:'+(COLORS[t]||'#999')+'"></span>'+
|
| 308 |
t.replace(/_/g,' ')+(t==='declared'?' (dashed)':'')+'</div>';
|
| 309 |
+
h+='<div class="reset" data-reset="1">show all</div>';
|
| 310 |
document.getElementById('legend').innerHTML=h;})();
|
| 311 |
+
let filterMode=false; // false=show-all default; flips after first click
|
| 312 |
function applyFilter(){
|
| 313 |
if(!currentNetwork)return;
|
| 314 |
const edgesDS=currentNetwork.body.data.edges, nodesDS=currentNetwork.body.data.nodes;
|
|
|
|
| 323 |
const reset=ev.target.closest('[data-reset]');
|
| 324 |
if(reset){LEGEND_TYPES.forEach(t=>ACTIVE_TYPES.add(t));
|
| 325 |
document.querySelectorAll('#legend .lt').forEach(el=>el.classList.remove('off'));
|
| 326 |
+
filterMode=false;applyFilter();return;}
|
| 327 |
const lt=ev.target.closest('.lt');
|
| 328 |
if(!lt||!lt.dataset.type)return;
|
| 329 |
const t=lt.dataset.type;
|
| 330 |
+
if(!filterMode){
|
| 331 |
+
// first click → narrow to ONLY this type; subsequent clicks add more
|
| 332 |
+
ACTIVE_TYPES.clear();ACTIVE_TYPES.add(t);
|
| 333 |
+
document.querySelectorAll('#legend .lt').forEach(el=>el.classList.toggle('off', el.dataset.type!==t));
|
| 334 |
+
filterMode=true;
|
| 335 |
+
} else if(ACTIVE_TYPES.has(t)){
|
| 336 |
+
// already in view → remove
|
| 337 |
+
ACTIVE_TYPES.delete(t);lt.classList.add('off');
|
| 338 |
+
if(ACTIVE_TYPES.size===0){
|
| 339 |
+
// emptied → return to show-all
|
| 340 |
+
LEGEND_TYPES.forEach(x=>ACTIVE_TYPES.add(x));
|
| 341 |
+
document.querySelectorAll('#legend .lt').forEach(el=>el.classList.remove('off'));
|
| 342 |
+
filterMode=false;
|
| 343 |
+
}
|
| 344 |
+
} else {
|
| 345 |
+
// add to view
|
| 346 |
+
ACTIVE_TYPES.add(t);lt.classList.remove('off');
|
| 347 |
+
}
|
| 348 |
applyFilter();
|
| 349 |
});
|
| 350 |
|
|
|
|
| 352 |
function bars(rows,opts){
|
| 353 |
opts=opts||{};const max=Math.max(...rows.map(r=>r.n),1);let h='<table class="bars">';
|
| 354 |
for(const r of rows){const w=Math.round(100*r.n/max);
|
| 355 |
+
let lbl;
|
| 356 |
+
if(opts.type){
|
| 357 |
+
lbl='<span class="dot" style="background:'+(COLORS[r.t]||'#999')+'"></span>'+r.t.replace(/_/g,' ');
|
| 358 |
+
} else {
|
| 359 |
+
const isDataset=r.id.includes('/');
|
| 360 |
+
const hubUrl='https://huggingface.co/'+(isDataset?'datasets/':'')+r.id;
|
| 361 |
+
lbl=(isDataset?'<a data-ds="'+r.id+'">'+r.id+'</a>':'<span>'+r.id+'</span>')+
|
| 362 |
+
'<a class="hub" href="'+hubUrl+'" target="_blank" rel="noopener" title="open on HF Hub">↗</a>';
|
| 363 |
+
}
|
| 364 |
const col=opts.type?(COLORS[r.t]||'#999'):'var(--bar)';
|
| 365 |
h+='<tr><td class="lbl">'+lbl+'</td><td class="bar"><span style="width:'+w+'%;background:'+col+'"></span></td>'
|
| 366 |
+'<td class="num">'+r.n.toLocaleString()+'</td></tr>';}
|
|
|
|
| 454 |
let h='<h3>'+title+' · '+arr.length+'</h3>';
|
| 455 |
for(const x of arr){const t=x.primary_type||'';
|
| 456 |
h+='<div class="row">'+dot(t)+'<a data-ds="'+x.id+'">'+x.id+'</a>'+
|
| 457 |
+
'<a class="hub" href="https://huggingface.co/datasets/'+x.id+'" target="_blank" rel="noopener" title="open on HF Hub">↗</a>'+
|
| 458 |
(t?'<span class="ty">'+t.replace(/_/g,' ')+(x.size_ratio?' ×'+x.size_ratio:'')+'</span>':'')+'</div>';}
|
| 459 |
return h;
|
| 460 |
}
|