File size: 4,730 Bytes
dd079af
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { useEffect, useState } from 'react';
import { Link, NavLink, useLocation } from 'react-router-dom';
import { Download, Menu, X } from 'lucide-react';
import { api } from '../lib/api';
import { SearchBox } from './SearchBox';

export function Logo({ size = 28 }: { size?: number }) {
  // Three conformational "states" joined by transitions.
  return (
    <svg width={size} height={size} viewBox="0 0 32 32" aria-hidden className="logo-mark">
      <rect width="32" height="32" rx="8" className="logo-mark__bg" />
      <path d="M9 21.5 L16 10 L23 21.5" className="logo-mark__edge" />
      <path d="M9 21.5 H23" className="logo-mark__edge logo-mark__edge--soft" />
      <circle cx="16" cy="10" r="3.4" className="logo-mark__n1" />
      <circle cx="9" cy="21.5" r="3.4" className="logo-mark__n2" />
      <circle cx="23" cy="21.5" r="3.4" className="logo-mark__n3" />
    </svg>
  );
}

const NAV = [
  { to: '/browse', label: 'Browse' },
  { to: '/statistics', label: 'Statistics' },
  { to: '/docs', label: 'Docs' },
];

export function Header() {
  const location = useLocation();
  const [open, setOpen] = useState(false);
  const isHome = location.pathname === '/';

  useEffect(() => setOpen(false), [location.pathname, location.search]);

  return (
    <header className={`site-header ${isHome ? 'site-header--home' : ''}`}>
      <div className="site-header__inner">
        <Link to="/" className="brand" aria-label="MuSProt home">
          <Logo />
          <span className="brand__name">MuSProt</span>
        </Link>
        {!isHome && (
          <div className="site-header__search">
            <SearchBox placeholder="Search chain, entry, UniProt, function…" />
          </div>
        )}
        <nav className="site-nav" aria-label="Main">
          {NAV.map((n) => (
            <NavLink key={n.to} to={n.to} className={({ isActive }) => `site-nav__link ${isActive ? 'is-active' : ''}`}>
              {n.label}
            </NavLink>
          ))}
          <a href={api.downloadUrl} className="btn btn--primary btn--sm">
            <Download aria-hidden /> Download
          </a>
        </nav>
        <button
          type="button"
          className="menu-toggle"
          aria-label="Menu"
          aria-expanded={open}
          onClick={() => setOpen((o) => !o)}
        >
          {open ? <X aria-hidden /> : <Menu aria-hidden />}
        </button>
      </div>
      {open && (
        <div className="mobile-nav">
          {!isHome && <SearchBox placeholder="Search MuSProt…" />}
          {NAV.map((n) => (
            <NavLink key={n.to} to={n.to} className="mobile-nav__link">
              {n.label}
            </NavLink>
          ))}
          <a href={api.downloadUrl} className="btn btn--primary">
            <Download aria-hidden /> Download MuSProt.db
          </a>
        </div>
      )}
    </header>
  );
}

export function Footer() {
  return (
    <footer className="site-footer">
      <div className="site-footer__inner">
        <div className="site-footer__brand">
          <Link to="/" className="brand">
            <Logo size={24} />
            <span className="brand__name">MuSProt</span>
          </Link>
          <p>
            A million-scale database of experimentally observed conformational states and state transitions of proteins,
            built from the Protein Data Bank.
          </p>
        </div>
        <div className="site-footer__cols">
          <div>
            <h3>Explore</h3>
            <Link to="/browse">Browse sequences</Link>
            <Link to="/browse?min_states=5">Highly multistate</Link>
            <Link to="/chain/3vgk/A">Example chain</Link>
          </div>
          <div>
            <h3>Data</h3>
            <Link to="/docs">Documentation</Link>
            <a href={api.downloadUrl}>Download SQLite</a>
            <a href="/api/docs" target="_blank" rel="noopener noreferrer">
              REST API
            </a>
          </div>
          <div>
            <h3>Sources</h3>
            <a href="https://www.rcsb.org" target="_blank" rel="noopener noreferrer">
              RCSB PDB
            </a>
            <a href="https://www.uniprot.org" target="_blank" rel="noopener noreferrer">
              UniProt
            </a>
            <a href="https://www.cathdb.info" target="_blank" rel="noopener noreferrer">
              CATH
            </a>
          </div>
        </div>
      </div>
      <div className="site-footer__base">
        <span>MuSProt · Multistate Protein Database</span>
        <a href="https://huggingface.co/spaces/omaib/MuSProt" target="_blank" rel="noopener noreferrer">
          Hosted on Hugging Face
        </a>
      </div>
    </footer>
  );
}