react-cheminfo 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/citation/core/formats.js +2 -3
- package/lib/citation/core/formats.js.map +1 -1
- package/lib/citation/core/segments.d.ts +8 -0
- package/lib/citation/core/segments.d.ts.map +1 -1
- package/lib/citation/core/segments.js +12 -3
- package/lib/citation/core/segments.js.map +1 -1
- package/lib/ecosystem/core/sites.d.ts +1 -1
- package/lib/ecosystem/core/sites.d.ts.map +1 -1
- package/lib/ecosystem/core/sites.js +27 -0
- package/lib/ecosystem/core/sites.js.map +1 -1
- package/lib/ecosystem/ui/EcosystemLinks.d.ts +33 -0
- package/lib/ecosystem/ui/EcosystemLinks.d.ts.map +1 -0
- package/lib/ecosystem/ui/EcosystemLinks.js +55 -0
- package/lib/ecosystem/ui/EcosystemLinks.js.map +1 -0
- package/lib/ecosystem/ui/EcosystemMenu.d.ts +3 -1
- package/lib/ecosystem/ui/EcosystemMenu.d.ts.map +1 -1
- package/lib/ecosystem/ui/EcosystemMenu.js +7 -84
- package/lib/ecosystem/ui/EcosystemMenu.js.map +1 -1
- package/lib/ecosystem/ui/SiteTile.d.ts +34 -0
- package/lib/ecosystem/ui/SiteTile.d.ts.map +1 -0
- package/lib/ecosystem/ui/SiteTile.js +78 -0
- package/lib/ecosystem/ui/SiteTile.js.map +1 -0
- package/lib/ecosystem/ui/index.d.ts +4 -0
- package/lib/ecosystem/ui/index.d.ts.map +1 -1
- package/lib/ecosystem/ui/index.js +2 -0
- package/lib/ecosystem/ui/index.js.map +1 -1
- package/lib/ecosystem/ui/marks.js +7 -1
- package/lib/ecosystem/ui/marks.js.map +1 -1
- package/package.json +4 -4
- package/src/citation/core/formats.ts +2 -3
- package/src/citation/core/segments.ts +11 -3
- package/src/ecosystem/core/sites.ts +31 -1
- package/src/ecosystem/ui/EcosystemLinks.tsx +114 -0
- package/src/ecosystem/ui/EcosystemMenu.tsx +8 -135
- package/src/ecosystem/ui/SiteTile.tsx +148 -0
- package/src/ecosystem/ui/index.ts +4 -0
- package/src/ecosystem/ui/marks.tsx +44 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
// Every mark is drawn in the same 32×32 box, on a plate of the site's own
|
|
3
|
-
// colour, so
|
|
3
|
+
// colour, so every mark of the family still reads as one row. Each keeps the
|
|
4
4
|
// geometry of that site's own logo where it has one, and carries the site's
|
|
5
5
|
// answering colour on exactly one element — which is what stops it collapsing
|
|
6
6
|
// into a flat shape at 16 px.
|
|
@@ -39,5 +39,11 @@ const GLYPHS = {
|
|
|
39
39
|
regexp: (alt) => (_jsx("text", { x: "50%", y: "53%", dominantBaseline: "central", textAnchor: "middle", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: "18", fontWeight: "700", fill: alt, children: ".*" })),
|
|
40
40
|
// Three residues of a chain, one of them the ligand you came for.
|
|
41
41
|
pdb: (alt) => (_jsxs(_Fragment, { children: [_jsx("g", { stroke: "#ffffff", strokeWidth: "1.9", strokeLinecap: "round", fill: "none", opacity: "0.85", children: _jsx("path", { d: "M9.2 12.4 22 15.4 15.6 23Z" }) }), _jsx("circle", { cx: "9.2", cy: "12.4", r: "3.1", fill: "#ffffff" }), _jsx("circle", { cx: "22", cy: "15.4", r: "3.1", fill: "#ffffff" }), _jsx("circle", { cx: "15.6", cy: "23", r: "3.1", fill: alt })] })),
|
|
42
|
+
// The site's own favicon: a spectrum, and the one signal being assigned.
|
|
43
|
+
elucidation: (alt) => (_jsxs(_Fragment, { children: [_jsx("path", { d: "M5 24h4l2-11 2 11h4l2-15 2 15h3l1.5-7 1.5 7", fill: "none", stroke: "#ffffff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("circle", { cx: "19", cy: "9", r: "3", fill: alt })] })),
|
|
44
|
+
// The site's own favicon: the double harpoon, one direction per colour.
|
|
45
|
+
equilibrium: (alt) => (_jsxs("g", { fill: "none", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M6.5 12.5h17l-4-4", stroke: "#ffffff" }), _jsx("path", { d: "M25.5 19.5h-17l4 4", stroke: alt })] })),
|
|
46
|
+
// A chain of monomers, one of them the comonomer written into it.
|
|
47
|
+
polycarp: (alt) => (_jsxs(_Fragment, { children: [_jsx("path", { d: "M6.5 20 12.5 12 19.5 20 25.5 12", fill: "none", stroke: "#ffffff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("circle", { cx: "6.5", cy: "20", r: "3", fill: "#ffffff" }), _jsx("circle", { cx: "12.5", cy: "12", r: "3", fill: "#ffffff" }), _jsx("circle", { cx: "19.5", cy: "20", r: "3", fill: alt }), _jsx("circle", { cx: "25.5", cy: "12", r: "3", fill: "#ffffff" })] })),
|
|
42
48
|
};
|
|
43
49
|
//# sourceMappingURL=marks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"marks.js","sourceRoot":"","sources":["../../../src/ecosystem/ui/marks.tsx"],"names":[],"mappings":";AAIA,0EAA0E;AAC1E,
|
|
1
|
+
{"version":3,"file":"marks.js","sourceRoot":"","sources":["../../../src/ecosystem/ui/marks.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-cheminfo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Assembled React components shared by the cheminfo sites: a Cite button and an ecosystem menu",
|
|
6
6
|
"author": "Luc Patiny",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
66
|
"@blueprintjs/core": "^6.18.0",
|
|
67
|
-
"react": ">=19",
|
|
68
|
-
"react-dom": ">=19",
|
|
69
|
-
"react-science": "^21.0.0 || ^22.0.0 || ^23.0.0"
|
|
67
|
+
"react": "^18.3.1 || >=19",
|
|
68
|
+
"react-dom": "^18.3.1 || >=19",
|
|
69
|
+
"react-science": "^20.6.2 || ^21.0.0 || ^22.0.0 || ^23.0.0"
|
|
70
70
|
},
|
|
71
71
|
"repository": {
|
|
72
72
|
"type": "git",
|
|
@@ -2,7 +2,7 @@ import type { Reference } from './reference.ts';
|
|
|
2
2
|
import { doiUrl } from './reference.ts';
|
|
3
3
|
import { renderHtml, renderMarkdown, renderText } from './render.ts';
|
|
4
4
|
import type { CitationStyleId } from './segments.ts';
|
|
5
|
-
import { citationSegments } from './segments.ts';
|
|
5
|
+
import { citationSegments, pageRange } from './segments.ts';
|
|
6
6
|
|
|
7
7
|
/** The formats the reference can be copied in. */
|
|
8
8
|
export type CitationFormatId =
|
|
@@ -113,7 +113,6 @@ export function formatCitation(
|
|
|
113
113
|
|
|
114
114
|
function formatBibTeX(reference: Reference): string {
|
|
115
115
|
const { title, journal, year, volume, issue, publisher, doi } = reference;
|
|
116
|
-
const { firstPage, lastPage } = reference;
|
|
117
116
|
const fields: Array<[string, string]> = [
|
|
118
117
|
['author', authorList(reference, ' and ')],
|
|
119
118
|
['title', title],
|
|
@@ -121,7 +120,7 @@ function formatBibTeX(reference: Reference): string {
|
|
|
121
120
|
['year', String(year)],
|
|
122
121
|
['volume', volume],
|
|
123
122
|
['number', issue],
|
|
124
|
-
['pages',
|
|
123
|
+
['pages', pageRange(reference, '--')],
|
|
125
124
|
['publisher', publisher],
|
|
126
125
|
['doi', doi],
|
|
127
126
|
['url', doiUrl(reference)],
|
|
@@ -164,7 +164,15 @@ function initialsFirst(reference: Reference, lastSeparator: string): string {
|
|
|
164
164
|
return `${names.slice(0, -1).join(', ')}${lastSeparator}${last}`;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
/**
|
|
168
|
+
* The pages of a work, as its styles write them.
|
|
169
|
+
* @param reference - The work being written.
|
|
170
|
+
* @param dash - What joins the two ends, an en dash in every style but BibTeX.
|
|
171
|
+
* @returns One number for a journal that paginates by article number, the
|
|
172
|
+
* range otherwise.
|
|
173
|
+
*/
|
|
174
|
+
export function pageRange(reference: Reference, dash = '–'): string {
|
|
175
|
+
const { firstPage, lastPage } = reference;
|
|
176
|
+
if (firstPage === lastPage) return firstPage;
|
|
177
|
+
return `${firstPage}${dash}${lastPage}`;
|
|
170
178
|
}
|
|
@@ -9,7 +9,10 @@ export type SiteId =
|
|
|
9
9
|
| 'tex'
|
|
10
10
|
| 'lcao'
|
|
11
11
|
| 'regexp'
|
|
12
|
-
| 'pdb'
|
|
12
|
+
| 'pdb'
|
|
13
|
+
| 'elucidation'
|
|
14
|
+
| 'equilibrium'
|
|
15
|
+
| 'polycarp';
|
|
13
16
|
|
|
14
17
|
export interface SiteName {
|
|
15
18
|
/** First half of the name, set in the site's leading colour. */
|
|
@@ -152,6 +155,33 @@ export const ECOSYSTEM_SITES: readonly EcosystemSite[] = [
|
|
|
152
155
|
brandAlt: '#b45309',
|
|
153
156
|
mark: { plate: '#2563eb', accent: '#fbbf24' },
|
|
154
157
|
},
|
|
158
|
+
{
|
|
159
|
+
id: 'elucidation',
|
|
160
|
+
name: { lead: 'elucidation', alt: 'cheminfo', dot: true },
|
|
161
|
+
host: 'elucidation.cheminfo.org',
|
|
162
|
+
tagline: 'A structure from a 1H NMR spectrum and a formula.',
|
|
163
|
+
brand: '#7e22ce',
|
|
164
|
+
brandAlt: '#b45309',
|
|
165
|
+
mark: { plate: '#7e22ce', accent: '#f59e0b' },
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'equilibrium',
|
|
169
|
+
name: { lead: 'Equi', alt: 'Librium' },
|
|
170
|
+
host: 'equilibrium.cheminfo.org',
|
|
171
|
+
tagline: 'Chemical equilibria: pH, speciation and titration curves.',
|
|
172
|
+
brand: '#0b5754',
|
|
173
|
+
brandAlt: '#a56600',
|
|
174
|
+
mark: { plate: '#0b5754', accent: '#f2a71b' },
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: 'polycarp',
|
|
178
|
+
name: { lead: 'Poly', alt: 'Carp' },
|
|
179
|
+
host: 'polycarp.cheminfo.org',
|
|
180
|
+
tagline: 'Predict the microstructure of a radical copolymerisation.',
|
|
181
|
+
brand: '#0d1d37',
|
|
182
|
+
brandAlt: '#0c9ba3',
|
|
183
|
+
mark: { plate: '#0d1d37', accent: '#0c9ba3' },
|
|
184
|
+
},
|
|
155
185
|
];
|
|
156
186
|
|
|
157
187
|
/**
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { CSSProperties, ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { SiteId } from '../core/sites.ts';
|
|
4
|
+
import { ECOSYSTEM_SITES, siteUrl } from '../core/sites.ts';
|
|
5
|
+
|
|
6
|
+
import { SiteTile } from './SiteTile.tsx';
|
|
7
|
+
|
|
8
|
+
// The menu of `EcosystemMenu` lives inside a popover, so its links only enter
|
|
9
|
+
// the document once somebody clicks. A crawler never clicks, so these are the
|
|
10
|
+
// links that carry the family: always rendered, in the page, followed — and
|
|
11
|
+
// each one carrying the name and the one line that says what it opens, as real
|
|
12
|
+
// text rather than a `title` a crawler ignores.
|
|
13
|
+
const SECTION_STYLE: CSSProperties = {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
gap: 8,
|
|
17
|
+
};
|
|
18
|
+
const HEADING_STYLE: CSSProperties = {
|
|
19
|
+
margin: 0,
|
|
20
|
+
color: '#5b6875',
|
|
21
|
+
fontSize: '0.75rem',
|
|
22
|
+
fontWeight: 600,
|
|
23
|
+
};
|
|
24
|
+
const GRID_STYLE: CSSProperties = {
|
|
25
|
+
display: 'grid',
|
|
26
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(15rem, 1fr))',
|
|
27
|
+
gap: 2,
|
|
28
|
+
};
|
|
29
|
+
const ROW_STYLE: CSSProperties = {
|
|
30
|
+
display: 'flex',
|
|
31
|
+
flexWrap: 'wrap',
|
|
32
|
+
alignItems: 'baseline',
|
|
33
|
+
gap: '0.35rem 0.9rem',
|
|
34
|
+
};
|
|
35
|
+
const LINK_STYLE: CSSProperties = {
|
|
36
|
+
fontSize: '0.8125rem',
|
|
37
|
+
fontWeight: 600,
|
|
38
|
+
textDecoration: 'none',
|
|
39
|
+
whiteSpace: 'nowrap',
|
|
40
|
+
};
|
|
41
|
+
const CURRENT_STYLE: CSSProperties = {
|
|
42
|
+
...LINK_STYLE,
|
|
43
|
+
color: '#8a96a3',
|
|
44
|
+
cursor: 'default',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export interface EcosystemLinksProps {
|
|
48
|
+
/**
|
|
49
|
+
* The site the visitor is already on, which is written but never linked.
|
|
50
|
+
* @default undefined
|
|
51
|
+
*/
|
|
52
|
+
currentSiteId?: SiteId;
|
|
53
|
+
/**
|
|
54
|
+
* What introduces the section.
|
|
55
|
+
* @default 'Our other tools'
|
|
56
|
+
*/
|
|
57
|
+
heading?: string;
|
|
58
|
+
/**
|
|
59
|
+
* How much of each site is written. `grid` gives every site its mark, its
|
|
60
|
+
* name and the line saying what it does — which is what a crawler reads to
|
|
61
|
+
* know what it is following, so it is the default. `row` writes the names
|
|
62
|
+
* only, for a footer with no room.
|
|
63
|
+
* @default 'grid'
|
|
64
|
+
*/
|
|
65
|
+
layout?: 'grid' | 'row';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Every site of the family as a plain link, for the footer of a site. It is
|
|
70
|
+
* what lets a crawler — and a visitor with no patience for menus — walk from
|
|
71
|
+
* one of our tools to the next, so it is rendered on every page rather than
|
|
72
|
+
* behind a button.
|
|
73
|
+
* @param props - The site it sits on, what introduces the section, and how much
|
|
74
|
+
* of each site is written.
|
|
75
|
+
* @returns The section of links.
|
|
76
|
+
*/
|
|
77
|
+
export function EcosystemLinks(props: EcosystemLinksProps): ReactElement {
|
|
78
|
+
const { currentSiteId, heading = 'Our other tools', layout = 'grid' } = props;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<nav className="ecosystem-links" style={SECTION_STYLE} aria-label={heading}>
|
|
82
|
+
<h2 style={HEADING_STYLE}>{heading}</h2>
|
|
83
|
+
{layout === 'grid' ? (
|
|
84
|
+
<div style={GRID_STYLE}>
|
|
85
|
+
{ECOSYSTEM_SITES.map((site) => (
|
|
86
|
+
<SiteTile
|
|
87
|
+
key={site.id}
|
|
88
|
+
site={site}
|
|
89
|
+
isCurrent={site.id === currentSiteId}
|
|
90
|
+
/>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
) : (
|
|
94
|
+
<div style={ROW_STYLE}>
|
|
95
|
+
{ECOSYSTEM_SITES.map((site) =>
|
|
96
|
+
site.id === currentSiteId ? (
|
|
97
|
+
<span key={site.id} style={CURRENT_STYLE}>
|
|
98
|
+
{site.host}
|
|
99
|
+
</span>
|
|
100
|
+
) : (
|
|
101
|
+
<a
|
|
102
|
+
key={site.id}
|
|
103
|
+
style={{ ...LINK_STYLE, color: site.brand }}
|
|
104
|
+
href={siteUrl(site)}
|
|
105
|
+
>
|
|
106
|
+
{site.host}
|
|
107
|
+
</a>
|
|
108
|
+
),
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
</nav>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { CSSProperties, ReactElement } from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import type {
|
|
5
|
-
import { ECOSYSTEM_SITES
|
|
4
|
+
import type { SiteId } from '../core/sites.ts';
|
|
5
|
+
import { ECOSYSTEM_SITES } from '../core/sites.ts';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { SiteTile } from './SiteTile.tsx';
|
|
8
8
|
|
|
9
9
|
const PANEL_STYLE: CSSProperties = {
|
|
10
10
|
display: 'flex',
|
|
@@ -23,42 +23,7 @@ const GRID_STYLE: CSSProperties = {
|
|
|
23
23
|
gridTemplateColumns: 'repeat(auto-fit, minmax(15rem, 1fr))',
|
|
24
24
|
gap: 2,
|
|
25
25
|
};
|
|
26
|
-
|
|
27
|
-
display: 'flex',
|
|
28
|
-
alignItems: 'center',
|
|
29
|
-
padding: '7px 8px',
|
|
30
|
-
border: '1px solid transparent',
|
|
31
|
-
borderRadius: 10,
|
|
32
|
-
color: '#16202c',
|
|
33
|
-
gap: 10,
|
|
34
|
-
textDecoration: 'none',
|
|
35
|
-
transition: 'background 120ms, border-color 120ms, transform 120ms',
|
|
36
|
-
};
|
|
37
|
-
const MARK_HOLDER_STYLE: CSSProperties = {
|
|
38
|
-
display: 'flex',
|
|
39
|
-
transition: 'transform 160ms',
|
|
40
|
-
};
|
|
41
|
-
const TEXT_STYLE: CSSProperties = { minWidth: 0 };
|
|
42
|
-
const NAME_STYLE: CSSProperties = {
|
|
43
|
-
display: 'flex',
|
|
44
|
-
alignItems: 'baseline',
|
|
45
|
-
fontSize: '0.9375rem',
|
|
46
|
-
fontWeight: 700,
|
|
47
|
-
letterSpacing: '-0.01em',
|
|
48
|
-
gap: 6,
|
|
49
|
-
whiteSpace: 'nowrap',
|
|
50
|
-
};
|
|
51
|
-
const HERE_STYLE: CSSProperties = {
|
|
52
|
-
fontSize: '0.625rem',
|
|
53
|
-
fontWeight: 700,
|
|
54
|
-
letterSpacing: '0.04em',
|
|
55
|
-
textTransform: 'uppercase',
|
|
56
|
-
};
|
|
57
|
-
const TAGLINE_STYLE: CSSProperties = {
|
|
58
|
-
color: '#5b6875',
|
|
59
|
-
fontSize: '0.75rem',
|
|
60
|
-
lineHeight: 1.35,
|
|
61
|
-
};
|
|
26
|
+
|
|
62
27
|
export interface EcosystemMenuProps {
|
|
63
28
|
/**
|
|
64
29
|
* The site the visitor is already on, which is shown but never linked.
|
|
@@ -69,7 +34,9 @@ export interface EcosystemMenuProps {
|
|
|
69
34
|
|
|
70
35
|
/**
|
|
71
36
|
* What the ecosystem button opens: every site of the family, each behind its
|
|
72
|
-
* own little logo and the two colours it owns.
|
|
37
|
+
* own little logo and the two colours it owns. It lives in a popover, so a
|
|
38
|
+
* crawler never reaches it — `EcosystemLinks` is what carries the family
|
|
39
|
+
* through the page itself.
|
|
73
40
|
* @param props - The site the visitor is already on.
|
|
74
41
|
* @returns The grid of sites.
|
|
75
42
|
*/
|
|
@@ -88,104 +55,10 @@ export function EcosystemMenu(props: EcosystemMenuProps): ReactElement {
|
|
|
88
55
|
isCurrent={site.id === currentSiteId}
|
|
89
56
|
isHovered={hovered === site.id}
|
|
90
57
|
onHover={setHovered}
|
|
58
|
+
newTab
|
|
91
59
|
/>
|
|
92
60
|
))}
|
|
93
61
|
</div>
|
|
94
62
|
</div>
|
|
95
63
|
);
|
|
96
64
|
}
|
|
97
|
-
|
|
98
|
-
interface SiteTileProps {
|
|
99
|
-
site: EcosystemSite;
|
|
100
|
-
isCurrent: boolean;
|
|
101
|
-
isHovered: boolean;
|
|
102
|
-
onHover: (id: SiteId | null) => void;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* One site: its mark, its name in its own colours, and what it does.
|
|
107
|
-
* @param props - The site, and whether it is the current or the hovered one.
|
|
108
|
-
* @returns The tile.
|
|
109
|
-
*/
|
|
110
|
-
function SiteTile(props: SiteTileProps): ReactElement {
|
|
111
|
-
const { site, isCurrent, isHovered, onHover } = props;
|
|
112
|
-
const lit = isHovered && !isCurrent;
|
|
113
|
-
|
|
114
|
-
const body = (
|
|
115
|
-
<>
|
|
116
|
-
<div
|
|
117
|
-
style={{
|
|
118
|
-
...MARK_HOLDER_STYLE,
|
|
119
|
-
transform: lit ? 'scale(1.08) rotate(-6deg)' : 'none',
|
|
120
|
-
}}
|
|
121
|
-
>
|
|
122
|
-
<SiteMark site={site} />
|
|
123
|
-
</div>
|
|
124
|
-
<div style={TEXT_STYLE}>
|
|
125
|
-
<div style={NAME_STYLE}>
|
|
126
|
-
<span>
|
|
127
|
-
<span style={{ color: site.brand }}>{site.name.lead}</span>
|
|
128
|
-
{site.name.dot ? <span style={{ color: '#8a96a3' }}>.</span> : null}
|
|
129
|
-
<span style={{ color: site.brandAlt }}>{site.name.alt}</span>
|
|
130
|
-
</span>
|
|
131
|
-
{isCurrent ? (
|
|
132
|
-
<span style={{ ...HERE_STYLE, color: site.brandAlt }}>
|
|
133
|
-
you are here
|
|
134
|
-
</span>
|
|
135
|
-
) : null}
|
|
136
|
-
</div>
|
|
137
|
-
<div style={TAGLINE_STYLE}>{site.tagline}</div>
|
|
138
|
-
</div>
|
|
139
|
-
</>
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const style: CSSProperties = {
|
|
143
|
-
...TILE_STYLE,
|
|
144
|
-
background: tint(site, isCurrent, lit),
|
|
145
|
-
borderColor: borderOf(site, isCurrent, lit),
|
|
146
|
-
transform: lit ? 'translateY(-1px)' : 'none',
|
|
147
|
-
cursor: isCurrent ? 'default' : 'pointer',
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
if (isCurrent) return <div style={style}>{body}</div>;
|
|
151
|
-
|
|
152
|
-
return (
|
|
153
|
-
<a
|
|
154
|
-
style={style}
|
|
155
|
-
href={siteUrl(site)}
|
|
156
|
-
title={site.host}
|
|
157
|
-
target="_blank"
|
|
158
|
-
rel="noreferrer"
|
|
159
|
-
onMouseEnter={() => {
|
|
160
|
-
onHover(site.id);
|
|
161
|
-
}}
|
|
162
|
-
onMouseLeave={() => {
|
|
163
|
-
onHover(null);
|
|
164
|
-
}}
|
|
165
|
-
onFocus={() => {
|
|
166
|
-
onHover(site.id);
|
|
167
|
-
}}
|
|
168
|
-
onBlur={() => {
|
|
169
|
-
onHover(null);
|
|
170
|
-
}}
|
|
171
|
-
>
|
|
172
|
-
{body}
|
|
173
|
-
</a>
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// A tile lights up in the colour of the site it opens, never in ours: the
|
|
178
|
-
// pointer moving down the grid is what makes the ten pairs of colours read.
|
|
179
|
-
function tint(site: EcosystemSite, isCurrent: boolean, lit: boolean): string {
|
|
180
|
-
if (isCurrent) return '#f5f7fa';
|
|
181
|
-
return lit ? `color-mix(in oklab, ${site.brand} 9%, white)` : 'transparent';
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function borderOf(
|
|
185
|
-
site: EcosystemSite,
|
|
186
|
-
isCurrent: boolean,
|
|
187
|
-
lit: boolean,
|
|
188
|
-
): string {
|
|
189
|
-
if (isCurrent) return '#dfe3e8';
|
|
190
|
-
return lit ? `color-mix(in oklab, ${site.brand} 32%, white)` : 'transparent';
|
|
191
|
-
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { CSSProperties, ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { EcosystemSite, SiteId } from '../core/sites.ts';
|
|
4
|
+
import { siteUrl } from '../core/sites.ts';
|
|
5
|
+
|
|
6
|
+
import { SiteMark } from './marks.tsx';
|
|
7
|
+
|
|
8
|
+
const TILE_STYLE: CSSProperties = {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
alignItems: 'center',
|
|
11
|
+
padding: '7px 8px',
|
|
12
|
+
border: '1px solid transparent',
|
|
13
|
+
borderRadius: 10,
|
|
14
|
+
color: '#16202c',
|
|
15
|
+
gap: 10,
|
|
16
|
+
textDecoration: 'none',
|
|
17
|
+
transition: 'background 120ms, border-color 120ms, transform 120ms',
|
|
18
|
+
};
|
|
19
|
+
const MARK_HOLDER_STYLE: CSSProperties = {
|
|
20
|
+
display: 'flex',
|
|
21
|
+
transition: 'transform 160ms',
|
|
22
|
+
};
|
|
23
|
+
const TEXT_STYLE: CSSProperties = { minWidth: 0 };
|
|
24
|
+
const NAME_STYLE: CSSProperties = {
|
|
25
|
+
display: 'flex',
|
|
26
|
+
alignItems: 'baseline',
|
|
27
|
+
fontSize: '0.9375rem',
|
|
28
|
+
fontWeight: 700,
|
|
29
|
+
letterSpacing: '-0.01em',
|
|
30
|
+
gap: 6,
|
|
31
|
+
whiteSpace: 'nowrap',
|
|
32
|
+
};
|
|
33
|
+
const HERE_STYLE: CSSProperties = {
|
|
34
|
+
fontSize: '0.625rem',
|
|
35
|
+
fontWeight: 700,
|
|
36
|
+
letterSpacing: '0.04em',
|
|
37
|
+
textTransform: 'uppercase',
|
|
38
|
+
};
|
|
39
|
+
const TAGLINE_STYLE: CSSProperties = {
|
|
40
|
+
color: '#5b6875',
|
|
41
|
+
fontSize: '0.75rem',
|
|
42
|
+
lineHeight: 1.35,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export interface SiteTileProps {
|
|
46
|
+
/** The site the tile opens. */
|
|
47
|
+
site: EcosystemSite;
|
|
48
|
+
/** Whether this is the site the visitor is already on. */
|
|
49
|
+
isCurrent: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Whether the pointer is on it, which lights it in its own colour.
|
|
52
|
+
* @default false
|
|
53
|
+
*/
|
|
54
|
+
isHovered?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Told which site the pointer moved onto, or null when it left.
|
|
57
|
+
* @default undefined
|
|
58
|
+
*/
|
|
59
|
+
onHover?: (id: SiteId | null) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Whether the link opens a tab of its own. A menu the visitor opened on
|
|
62
|
+
* purpose does; a footer does not.
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
newTab?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* One site: its mark, its name in its own colours, and what it does. The name
|
|
70
|
+
* and the tagline are real text — that is what a crawler reads to know what it
|
|
71
|
+
* is following, so neither is ever a `title` attribute.
|
|
72
|
+
* @param props - The site, whether it is the current or the hovered one, and
|
|
73
|
+
* how the link opens.
|
|
74
|
+
* @returns The tile.
|
|
75
|
+
*/
|
|
76
|
+
export function SiteTile(props: SiteTileProps): ReactElement {
|
|
77
|
+
const { site, isCurrent, isHovered = false, onHover, newTab = false } = props;
|
|
78
|
+
const lit = isHovered && !isCurrent;
|
|
79
|
+
|
|
80
|
+
const body = (
|
|
81
|
+
<>
|
|
82
|
+
<div
|
|
83
|
+
style={{
|
|
84
|
+
...MARK_HOLDER_STYLE,
|
|
85
|
+
transform: lit ? 'scale(1.08) rotate(-6deg)' : 'none',
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<SiteMark site={site} />
|
|
89
|
+
</div>
|
|
90
|
+
<div style={TEXT_STYLE}>
|
|
91
|
+
<div style={NAME_STYLE}>
|
|
92
|
+
<span>
|
|
93
|
+
<span style={{ color: site.brand }}>{site.name.lead}</span>
|
|
94
|
+
{site.name.dot ? <span style={{ color: '#8a96a3' }}>.</span> : null}
|
|
95
|
+
<span style={{ color: site.brandAlt }}>{site.name.alt}</span>
|
|
96
|
+
</span>
|
|
97
|
+
{isCurrent ? (
|
|
98
|
+
<span style={{ ...HERE_STYLE, color: site.brandAlt }}>
|
|
99
|
+
you are here
|
|
100
|
+
</span>
|
|
101
|
+
) : null}
|
|
102
|
+
</div>
|
|
103
|
+
<div style={TAGLINE_STYLE}>{site.tagline}</div>
|
|
104
|
+
</div>
|
|
105
|
+
</>
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const style: CSSProperties = {
|
|
109
|
+
...TILE_STYLE,
|
|
110
|
+
background: tint(site, isCurrent, lit),
|
|
111
|
+
borderColor: borderOf(site, isCurrent, lit),
|
|
112
|
+
transform: lit ? 'translateY(-1px)' : 'none',
|
|
113
|
+
cursor: isCurrent ? 'default' : 'pointer',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (isCurrent) return <div style={style}>{body}</div>;
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<a
|
|
120
|
+
style={style}
|
|
121
|
+
href={siteUrl(site)}
|
|
122
|
+
target={newTab ? '_blank' : undefined}
|
|
123
|
+
rel={newTab ? 'noreferrer' : undefined}
|
|
124
|
+
onMouseEnter={() => onHover?.(site.id)}
|
|
125
|
+
onMouseLeave={() => onHover?.(null)}
|
|
126
|
+
onFocus={() => onHover?.(site.id)}
|
|
127
|
+
onBlur={() => onHover?.(null)}
|
|
128
|
+
>
|
|
129
|
+
{body}
|
|
130
|
+
</a>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// A tile lights up in the colour of the site it opens, never in ours: the
|
|
135
|
+
// pointer moving down the grid is what makes the pairs of colours read.
|
|
136
|
+
function tint(site: EcosystemSite, isCurrent: boolean, lit: boolean): string {
|
|
137
|
+
if (isCurrent) return '#f5f7fa';
|
|
138
|
+
return lit ? `color-mix(in oklab, ${site.brand} 9%, white)` : 'transparent';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function borderOf(
|
|
142
|
+
site: EcosystemSite,
|
|
143
|
+
isCurrent: boolean,
|
|
144
|
+
lit: boolean,
|
|
145
|
+
): string {
|
|
146
|
+
if (isCurrent) return '#dfe3e8';
|
|
147
|
+
return lit ? `color-mix(in oklab, ${site.brand} 32%, white)` : 'transparent';
|
|
148
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export type { EcosystemButtonProps } from './EcosystemButton.tsx';
|
|
2
2
|
export { EcosystemButton } from './EcosystemButton.tsx';
|
|
3
|
+
export type { EcosystemLinksProps } from './EcosystemLinks.tsx';
|
|
4
|
+
export { EcosystemLinks } from './EcosystemLinks.tsx';
|
|
3
5
|
export type { EcosystemMenuProps } from './EcosystemMenu.tsx';
|
|
4
6
|
export { EcosystemMenu } from './EcosystemMenu.tsx';
|
|
5
7
|
export type { SiteMarkProps } from './marks.tsx';
|
|
6
8
|
export { SiteMark } from './marks.tsx';
|
|
9
|
+
export type { SiteTileProps } from './SiteTile.tsx';
|
|
10
|
+
export { SiteTile } from './SiteTile.tsx';
|
|
@@ -3,7 +3,7 @@ import type { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
|
3
3
|
import type { EcosystemSite, SiteId } from '../core/sites.ts';
|
|
4
4
|
|
|
5
5
|
// Every mark is drawn in the same 32×32 box, on a plate of the site's own
|
|
6
|
-
// colour, so
|
|
6
|
+
// colour, so every mark of the family still reads as one row. Each keeps the
|
|
7
7
|
// geometry of that site's own logo where it has one, and carries the site's
|
|
8
8
|
// answering colour on exactly one element — which is what stops it collapsing
|
|
9
9
|
// into a flat shape at 16 px.
|
|
@@ -204,4 +204,47 @@ const GLYPHS: Record<SiteId, (alt: string) => ReactNode> = {
|
|
|
204
204
|
<circle cx="15.6" cy="23" r="3.1" fill={alt} />
|
|
205
205
|
</>
|
|
206
206
|
),
|
|
207
|
+
// The site's own favicon: a spectrum, and the one signal being assigned.
|
|
208
|
+
elucidation: (alt) => (
|
|
209
|
+
<>
|
|
210
|
+
<path
|
|
211
|
+
d="M5 24h4l2-11 2 11h4l2-15 2 15h3l1.5-7 1.5 7"
|
|
212
|
+
fill="none"
|
|
213
|
+
stroke="#ffffff"
|
|
214
|
+
strokeWidth="2"
|
|
215
|
+
strokeLinecap="round"
|
|
216
|
+
strokeLinejoin="round"
|
|
217
|
+
/>
|
|
218
|
+
<circle cx="19" cy="9" r="3" fill={alt} />
|
|
219
|
+
</>
|
|
220
|
+
),
|
|
221
|
+
// The site's own favicon: the double harpoon, one direction per colour.
|
|
222
|
+
equilibrium: (alt) => (
|
|
223
|
+
<g
|
|
224
|
+
fill="none"
|
|
225
|
+
strokeWidth="2.5"
|
|
226
|
+
strokeLinecap="round"
|
|
227
|
+
strokeLinejoin="round"
|
|
228
|
+
>
|
|
229
|
+
<path d="M6.5 12.5h17l-4-4" stroke="#ffffff" />
|
|
230
|
+
<path d="M25.5 19.5h-17l4 4" stroke={alt} />
|
|
231
|
+
</g>
|
|
232
|
+
),
|
|
233
|
+
// A chain of monomers, one of them the comonomer written into it.
|
|
234
|
+
polycarp: (alt) => (
|
|
235
|
+
<>
|
|
236
|
+
<path
|
|
237
|
+
d="M6.5 20 12.5 12 19.5 20 25.5 12"
|
|
238
|
+
fill="none"
|
|
239
|
+
stroke="#ffffff"
|
|
240
|
+
strokeWidth="2"
|
|
241
|
+
strokeLinecap="round"
|
|
242
|
+
strokeLinejoin="round"
|
|
243
|
+
/>
|
|
244
|
+
<circle cx="6.5" cy="20" r="3" fill="#ffffff" />
|
|
245
|
+
<circle cx="12.5" cy="12" r="3" fill="#ffffff" />
|
|
246
|
+
<circle cx="19.5" cy="20" r="3" fill={alt} />
|
|
247
|
+
<circle cx="25.5" cy="12" r="3" fill="#ffffff" />
|
|
248
|
+
</>
|
|
249
|
+
),
|
|
207
250
|
};
|