Download frontend/src/components/SequenceTable.tsx from omaib/MuSProt: direct link, hf CLI and curl.
- Browser
- Download file 3.65 kB
-
https://huggingface.co/spaces/omaib/MuSProt/resolve/main/frontend/src/components/SequenceTable.tsx
- Command line
-
hf download hf://spaces/omaib/MuSProt/frontend/src/components/SequenceTable.tsx
-
curl -L -o SequenceTable.tsx https://huggingface.co/spaces/omaib/MuSProt/resolve/main/frontend/src/components/SequenceTable.tsx
3.65 kB
| import { useNavigate } from 'react-router-dom'; | |
| import type { SequenceSummary } from '../lib/api'; | |
| import { cleanFunction, fmtInt, methodShort, sequencePath, year } from '../lib/format'; | |
| import { MiniSplit } from './charts'; | |
| export type SortKey = 'sequence' | 'length' | 'observations' | 'states' | 'entries' | 'released'; | |
| function Th({ | |
| k, | |
| label, | |
| sort, | |
| order, | |
| onSort, | |
| num, | |
| }: { | |
| k: SortKey; | |
| label: string; | |
| sort?: SortKey; | |
| order?: 'asc' | 'desc'; | |
| onSort?: (k: SortKey) => void; | |
| num?: boolean; | |
| }) { | |
| const active = sort === k; | |
| if (!onSort) return <th className={num ? 'num' : ''}>{label}</th>; | |
| return ( | |
| <th className={num ? 'num' : ''} aria-sort={active ? (order === 'asc' ? 'ascending' : 'descending') : 'none'}> | |
| <button type="button" className={`th-sort ${active ? 'is-active' : ''}`} onClick={() => onSort(k)}> | |
| {label} | |
| <span className="th-sort__arrow" aria-hidden> | |
| {active ? (order === 'asc' ? '↑' : '↓') : '↕'} | |
| </span> | |
| </button> | |
| </th> | |
| ); | |
| } | |
| export function SequenceTable({ | |
| items, | |
| sort, | |
| order, | |
| onSort, | |
| }: { | |
| items: SequenceSummary[]; | |
| sort?: SortKey; | |
| order?: 'asc' | 'desc'; | |
| onSort?: (k: SortKey) => void; | |
| }) { | |
| const navigate = useNavigate(); | |
| const p = { sort, order, onSort }; | |
| return ( | |
| <div className="table-wrap"> | |
| <table className="table table--rows"> | |
| <thead> | |
| <tr> | |
| <Th k="sequence" label="Sequence" {...p} /> | |
| <th>Function</th> | |
| <Th k="length" label="Length" num {...p} /> | |
| <Th k="observations" label="Obs." num {...p} /> | |
| <Th k="states" label="States" num {...p} /> | |
| <Th k="entries" label="Entries" num {...p} /> | |
| <th>Apo / holo</th> | |
| <th>Methods</th> | |
| <Th k="released" label="Latest" num {...p} /> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {items.map((s) => ( | |
| <tr | |
| key={s.sequence_id} | |
| className="is-clickable" | |
| onClick={(e) => { | |
| if ((e.target as HTMLElement).closest('a')) return; | |
| navigate(sequencePath(s.sequence_id)); | |
| }} | |
| > | |
| <td> | |
| <a | |
| href={sequencePath(s.sequence_id)} | |
| className="mono strong" | |
| onClick={(e) => { | |
| e.preventDefault(); | |
| navigate(sequencePath(s.sequence_id)); | |
| }} | |
| > | |
| {s.sequence_id} | |
| </a> | |
| <div className="sub mono">{s.uniprot_id ?? 'no UniProt'}</div> | |
| </td> | |
| <td className="fn-cell"> | |
| <span title={s.top_function ?? undefined}>{s.top_function ? cleanFunction(s.top_function) : '—'}</span> | |
| </td> | |
| <td className="num tabular">{fmtInt(s.length)}</td> | |
| <td className="num tabular">{fmtInt(s.n_obs)}</td> | |
| <td className="num tabular strong">{fmtInt(s.n_states)}</td> | |
| <td className="num tabular">{fmtInt(s.n_entries)}</td> | |
| <td> | |
| <MiniSplit | |
| a={s.n_apo} | |
| b={s.n_holo} | |
| colorA="var(--apo)" | |
| colorB="var(--holo)" | |
| title={`${s.n_apo} apo · ${s.n_holo} holo`} | |
| /> | |
| </td> | |
| <td className="methods-cell">{s.methods.slice(0, 2).map(methodShort).join(', ')}</td> | |
| <td className="num tabular">{year(s.last_release)}</td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| </div> | |
| ); | |
| } | |