ouroboros / source /src /Dataset.tsx
REXWind's picture
Publish the Ouroboros scientific showcase to CladeTeam
03677d0 verified
Raw History Blame Contribute Delete
4 kB
import {useRef,useState} from 'react';
export type AnnotationCase={id:string;text:string;source:string;url:string;origin:string;name?:string};
export type AnnotationRow={key:string;count:number;coverage:number;share:number;example?:AnnotationCase};
export type DatasetData={scope:string;scopeConfirmed:boolean;keys:number;recordKeyPairs:number;beyondTopFiveShare:number;rows:AnnotationRow[]};
const number=(n:number)=>n.toLocaleString('en-US');
const pct=(n:number)=>n.toFixed(n<.01?6:2)+'%';
export function Dataset({data}:{data:DatasetData}){
const [selected,setSelected]=useState('function'),[query,setQuery]=useState('');
const rail=useRef<HTMLDivElement>(null);
const active=data.rows.find(r=>r.key===selected)!;
const rows=data.rows.filter(r=>r.key.toLowerCase().includes(query.toLowerCase()));
const scroll=(sign:number)=>rail.current?.scrollBy({left:sign*rail.current.clientWidth*.7,behavior:window.matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth'});
return <section id="dataset" className="section dataset-section dataset-compact">
<div className="section-heading"><div><p className="eyebrow"><span>02</span> THE LANGUAGE OF THE DATA</p><h2>Many ways to describe <em>a protein.</em></h2></div><p className="section-deck">Scroll through the keys.<br/>{' '}Select one to explore an annotation.</p></div>
<div className="compact-atlas"><div className="compact-stats"><span><strong>{data.keys}</strong> annotation types</span><span><strong>{(data.recordKeyPairs/1e6).toFixed(2)}M</strong> record–key pairs</span><span><strong>{data.beyondTopFiveShare.toFixed(1)}%</strong> beyond the top five keys</span></div>
<div className="key-explorer"><div className="key-rail-toolbar"><span className="micro">{query?rows.length+' MATCHING KEYS':'29 KEYS · SCROLL TO EXPLORE'}</span><label><span className="sr-only">Search annotation keys</span><input value={query} onChange={e=>setQuery(e.target.value)} placeholder="Find a key…"/></label><div><button onClick={()=>scroll(-1)} aria-label="Scroll keys left">←</button><button onClick={()=>scroll(1)} aria-label="Scroll keys right">→</button></div></div>
<div className="key-rail" ref={rail} role="group" aria-label="Annotation keys">{rows.map(r=><button key={r.key} aria-pressed={selected===r.key} onClick={e=>{setSelected(r.key);e.currentTarget.scrollIntoView({block:'nearest',inline:'nearest',behavior:window.matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth'});}}><span>{r.key}</span><small>{pct(r.coverage)}</small></button>)}</div>{!rows.length&&<p className="atlas-empty">No matching key.</p>}
<div className="key-case" aria-live="polite"><div className="key-case-stat"><span className="micro">ANNOTATION KEY</span><h3>{active.key}</h3><strong>{pct(active.coverage)}<small>record coverage</small></strong><span>{number(active.count)} records</span></div><div className="key-case-example" key={active.key}><div className="key-case-label"><span className="micro">ANNOTATION EXAMPLE</span>{active.example&&<a href={active.example.url} target="_blank" rel="noreferrer">{active.example.id} ↗</a>}</div>{active.example?<><blockquote>{active.example.text}</blockquote><div className="key-case-source">{active.example.name&&<strong>{active.example.name} · </strong>}{active.example.source}</div></>:<p className="case-pending">The source annotation is being prepared.</p>}</div></div>
</div>
</div><details className="dataset-notes"><summary>Definitions & source data</summary><p>{data.scope}. Counts total {number(data.recordKeyPairs)} record–key pairs across {data.keys} keys. Records may carry multiple keys; coverage overlaps. This describes annotation breadth, not sequence or species diversity. Case sources are identified individually.</p><div className="source-links"><a href="/data/annotation_key_counts.csv" download>↓ Original key statistics</a><a href="/data/annotation-stats.json" download>↓ Statistics & examples</a></div></details>
</section>;
}