Spaces:
Running
Running
| import { useMemo } from 'react' | |
| import { MODELS, displayAuroc } from '../data/models' | |
| import { | |
| rankModels, | |
| distinctEnzymes, | |
| distinctProtocols, | |
| distinctSpecies, | |
| type Conditions, | |
| } from '../lib/rankModels' | |
| interface ConditionMatcherProps { | |
| conditions: Conditions | |
| onChange: (c: Conditions) => void | |
| onPick: (code: string) => void // set the active model | |
| selected: string // active model code (to highlight) | |
| } | |
| // Let the user describe how their peptides were generated (species / enzyme / MS | |
| // protocol) and rank the models by condition similarity, species first, then | |
| // enzyme, then protocol. The top matches are offered as one-click model choices. | |
| export default function ConditionMatcher({ | |
| conditions, | |
| onChange, | |
| onPick, | |
| selected, | |
| }: ConditionMatcherProps) { | |
| const enzymes = useMemo(() => distinctEnzymes(MODELS), []) | |
| const protocols = useMemo(() => distinctProtocols(MODELS), []) | |
| const species = useMemo(() => distinctSpecies(MODELS), []) | |
| const anySet = !!(conditions.species || conditions.enzyme || conditions.protocol) | |
| const ranked = useMemo( | |
| () => (anySet ? rankModels(MODELS, conditions) : []), | |
| [conditions, anySet], | |
| ) | |
| const top = ranked.slice(0, 3) | |
| return ( | |
| <div className="field"> | |
| <div className="field-label"> | |
| Match my conditions{' '} | |
| <span className="field-hint"> | |
| How were your peptides made? We rank models by species, then enzyme, then protocol. | |
| </span> | |
| </div> | |
| <div className="cond-selects"> | |
| <label className="cond-select"> | |
| <span>Species</span> | |
| <select | |
| value={conditions.species ?? ''} | |
| onChange={(e) => onChange({ ...conditions, species: e.target.value || undefined })} | |
| > | |
| <option value="">Any</option> | |
| {species.map((s) => ( | |
| <option key={s.key} value={s.key}> | |
| {s.label} | |
| </option> | |
| ))} | |
| <option value="other">Other</option> | |
| </select> | |
| </label> | |
| <label className="cond-select"> | |
| <span>Enzyme</span> | |
| <select | |
| value={conditions.enzyme ?? ''} | |
| onChange={(e) => onChange({ ...conditions, enzyme: e.target.value || undefined })} | |
| > | |
| <option value="">Any</option> | |
| {enzymes.map((x) => ( | |
| <option key={x} value={x}> | |
| {x} | |
| </option> | |
| ))} | |
| <option value="other">Other</option> | |
| </select> | |
| </label> | |
| <label className="cond-select"> | |
| <span>MS protocol</span> | |
| <select | |
| value={conditions.protocol ?? ''} | |
| onChange={(e) => onChange({ ...conditions, protocol: e.target.value || undefined })} | |
| > | |
| <option value="">Any</option> | |
| {protocols.map((x) => ( | |
| <option key={x} value={x}> | |
| {x} | |
| </option> | |
| ))} | |
| <option value="other">Other</option> | |
| </select> | |
| </label> | |
| </div> | |
| {anySet && ( | |
| <div className="cond-ranked"> | |
| <div className="muted" style={{ fontSize: 'var(--text-xs)', fontWeight: 600, margin: '4px 0' }}> | |
| Best-matched models | |
| </div> | |
| {top.map((m, i) => { | |
| const on = selected === m.code | |
| return ( | |
| <button | |
| key={m.code} | |
| type="button" | |
| className={'cond-rank-row' + (on ? ' selected' : '')} | |
| onClick={() => onPick(m.code)} | |
| title="Use this model" | |
| > | |
| <span className="cond-rank-pos">#{i + 1}</span> | |
| <span className="cond-rank-body"> | |
| <span className="cond-rank-title"> | |
| <span className="mono">{m.code}</span> · {m.species} · {m.enzyme} | |
| </span> | |
| <span className="cond-rank-tags"> | |
| <span className={'tag' + (m.speciesMatch ? ' tag-on' : '')}>species</span> | |
| <span className={'tag' + (m.enzymeMatch ? ' tag-on' : '')}>enzyme</span> | |
| <span className={'tag' + (m.protocolMatch ? ' tag-on' : '')}>protocol</span> | |
| <span className="muted" style={{ fontSize: 'var(--text-xs)' }}> | |
| AUROC {displayAuroc(m)} | |
| </span> | |
| </span> | |
| </span> | |
| {on && <span className="badge badge-good">Selected</span>} | |
| </button> | |
| ) | |
| })} | |
| </div> | |
| )} | |
| </div> | |
| ) | |
| } | |