react-cheminfo 0.58.0 → 0.60.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 +7 -2
- package/lib/about/ui/AboutBuildDetails.d.ts +15 -0
- package/lib/about/ui/AboutBuildDetails.d.ts.map +1 -0
- package/lib/about/ui/AboutBuildDetails.js +17 -0
- package/lib/about/ui/AboutBuildDetails.js.map +1 -0
- package/lib/about/ui/AboutPage.d.ts +8 -0
- package/lib/about/ui/AboutPage.d.ts.map +1 -1
- package/lib/about/ui/AboutPage.js +12 -3
- package/lib/about/ui/AboutPage.js.map +1 -1
- package/lib/about/ui/AboutSignIn.d.ts +33 -0
- package/lib/about/ui/AboutSignIn.d.ts.map +1 -0
- package/lib/about/ui/AboutSignIn.js +28 -0
- package/lib/about/ui/AboutSignIn.js.map +1 -0
- package/lib/about/ui/AboutVersion.d.ts +4 -2
- package/lib/about/ui/AboutVersion.d.ts.map +1 -1
- package/lib/about/ui/AboutVersion.js +21 -3
- package/lib/about/ui/AboutVersion.js.map +1 -1
- package/lib/about/ui/index.d.ts +2 -2
- package/lib/about/ui/index.d.ts.map +1 -1
- package/lib/about/ui/index.js +1 -1
- package/lib/about/ui/index.js.map +1 -1
- package/lib/chart/ui/ChartTrackingLayer.js +3 -1
- package/lib/chart/ui/ChartTrackingLayer.js.map +1 -1
- package/lib/chart/ui/useChartSize.d.ts +16 -0
- package/lib/chart/ui/useChartSize.d.ts.map +1 -1
- package/lib/chart/ui/useChartSize.js +34 -1
- package/lib/chart/ui/useChartSize.js.map +1 -1
- package/lib/chrome/ui/NavEntry.d.ts +16 -0
- package/lib/chrome/ui/NavEntry.d.ts.map +1 -0
- package/lib/chrome/ui/NavEntry.js +27 -0
- package/lib/chrome/ui/NavEntry.js.map +1 -0
- package/lib/chrome/ui/SiteHeader.d.ts +11 -4
- package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
- package/lib/chrome/ui/SiteHeader.js +4 -26
- package/lib/chrome/ui/SiteHeader.js.map +1 -1
- package/lib/core.d.ts +2 -0
- package/lib/core.d.ts.map +1 -1
- package/lib/core.js +2 -0
- package/lib/core.js.map +1 -1
- package/lib/ecosystem/core/types.d.ts +1 -1
- package/lib/ecosystem/core/types.d.ts.map +1 -1
- package/lib/ecosystem/core/unlistedSites.d.ts.map +1 -1
- package/lib/ecosystem/core/unlistedSites.js +14 -0
- package/lib/ecosystem/core/unlistedSites.js.map +1 -1
- package/lib/ecosystem/ui/glyphs.d.ts.map +1 -1
- package/lib/ecosystem/ui/glyphs.js +3 -0
- package/lib/ecosystem/ui/glyphs.js.map +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/locales/de.json +36 -3
- package/lib/locales/en.json +36 -3
- package/lib/locales/es.json +36 -3
- package/lib/locales/fr.json +36 -3
- package/lib/locales/it.json +36 -3
- package/lib/moleculesSources/core/index.d.ts +3 -0
- package/lib/moleculesSources/core/index.d.ts.map +1 -0
- package/lib/moleculesSources/core/index.js +2 -0
- package/lib/moleculesSources/core/index.js.map +1 -0
- package/lib/moleculesSources/core/moleculesSources.d.ts +55 -0
- package/lib/moleculesSources/core/moleculesSources.d.ts.map +1 -0
- package/lib/moleculesSources/core/moleculesSources.js +63 -0
- package/lib/moleculesSources/core/moleculesSources.js.map +1 -0
- package/lib/moleculesSources/ui/MoleculesSourceBadge.d.ts +18 -0
- package/lib/moleculesSources/ui/MoleculesSourceBadge.d.ts.map +1 -0
- package/lib/moleculesSources/ui/MoleculesSourceBadge.js +22 -0
- package/lib/moleculesSources/ui/MoleculesSourceBadge.js.map +1 -0
- package/lib/moleculesSources/ui/SourceBadge.d.ts +57 -0
- package/lib/moleculesSources/ui/SourceBadge.d.ts.map +1 -0
- package/lib/moleculesSources/ui/SourceBadge.js +29 -0
- package/lib/moleculesSources/ui/SourceBadge.js.map +1 -0
- package/lib/moleculesSources/ui/index.d.ts +7 -0
- package/lib/moleculesSources/ui/index.d.ts.map +1 -0
- package/lib/moleculesSources/ui/index.js +4 -0
- package/lib/moleculesSources/ui/index.js.map +1 -0
- package/lib/moleculesSources/ui/useSourceLooks.d.ts +24 -0
- package/lib/moleculesSources/ui/useSourceLooks.d.ts.map +1 -0
- package/lib/moleculesSources/ui/useSourceLooks.js +53 -0
- package/lib/moleculesSources/ui/useSourceLooks.js.map +1 -0
- package/lib/moleculesToken/core/index.d.ts +4 -0
- package/lib/moleculesToken/core/index.d.ts.map +1 -0
- package/lib/moleculesToken/core/index.js +3 -0
- package/lib/moleculesToken/core/index.js.map +1 -0
- package/lib/moleculesToken/core/moleculesToken.d.ts +101 -0
- package/lib/moleculesToken/core/moleculesToken.d.ts.map +1 -0
- package/lib/moleculesToken/core/moleculesToken.js +137 -0
- package/lib/moleculesToken/core/moleculesToken.js.map +1 -0
- package/lib/moleculesToken/core/tokenFix.d.ts +18 -0
- package/lib/moleculesToken/core/tokenFix.d.ts.map +1 -0
- package/lib/moleculesToken/core/tokenFix.js +27 -0
- package/lib/moleculesToken/core/tokenFix.js.map +1 -0
- package/lib/moleculesToken/ui/MoleculesTokenFix.d.ts +22 -0
- package/lib/moleculesToken/ui/MoleculesTokenFix.d.ts.map +1 -0
- package/lib/moleculesToken/ui/MoleculesTokenFix.js +35 -0
- package/lib/moleculesToken/ui/MoleculesTokenFix.js.map +1 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.d.ts +37 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.d.ts.map +1 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.js +56 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.js.map +1 -0
- package/lib/moleculesToken/ui/index.d.ts +5 -0
- package/lib/moleculesToken/ui/index.d.ts.map +1 -0
- package/lib/moleculesToken/ui/index.js +3 -0
- package/lib/moleculesToken/ui/index.js.map +1 -0
- package/lib/moleculesToken/ui/tokenDetails.d.ts +13 -0
- package/lib/moleculesToken/ui/tokenDetails.d.ts.map +1 -0
- package/lib/moleculesToken/ui/tokenDetails.js +73 -0
- package/lib/moleculesToken/ui/tokenDetails.js.map +1 -0
- package/lib/ui.d.ts +2 -0
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +2 -0
- package/lib/ui.js.map +1 -1
- package/package.json +1 -1
- package/src/about/ui/AboutBuildDetails.tsx +45 -0
- package/src/about/ui/AboutPage.tsx +27 -13
- package/src/about/ui/AboutSignIn.tsx +63 -0
- package/src/about/ui/AboutVersion.tsx +27 -9
- package/src/about/ui/index.ts +2 -2
- package/src/chart/ui/ChartTrackingLayer.tsx +3 -1
- package/src/chart/ui/useChartSize.ts +44 -1
- package/src/chrome/ui/NavEntry.tsx +41 -0
- package/src/chrome/ui/SiteHeader.tsx +15 -41
- package/src/core.ts +2 -0
- package/src/ecosystem/core/types.ts +2 -1
- package/src/ecosystem/core/unlistedSites.ts +14 -0
- package/src/ecosystem/ui/glyphs.tsx +10 -0
- package/src/locales/de.json +36 -3
- package/src/locales/en.json +36 -3
- package/src/locales/es.json +36 -3
- package/src/locales/fr.json +36 -3
- package/src/locales/it.json +36 -3
- package/src/moleculesSources/core/index.ts +11 -0
- package/src/moleculesSources/core/moleculesSources.ts +102 -0
- package/src/moleculesSources/ui/MoleculesSourceBadge.tsx +44 -0
- package/src/moleculesSources/ui/SourceBadge.tsx +117 -0
- package/src/moleculesSources/ui/index.ts +6 -0
- package/src/moleculesSources/ui/useSourceLooks.ts +70 -0
- package/src/moleculesToken/core/index.ts +12 -0
- package/src/moleculesToken/core/moleculesToken.ts +256 -0
- package/src/moleculesToken/core/tokenFix.ts +37 -0
- package/src/moleculesToken/ui/MoleculesTokenFix.tsx +95 -0
- package/src/moleculesToken/ui/MoleculesTokenTable.tsx +175 -0
- package/src/moleculesToken/ui/index.ts +4 -0
- package/src/moleculesToken/ui/tokenDetails.tsx +91 -0
- package/src/ui.ts +2 -0
- package/styles/chrome.css +194 -0
- package/lib/about/ui/AboutBuild.d.ts +0 -18
- package/lib/about/ui/AboutBuild.d.ts.map +0 -1
- package/lib/about/ui/AboutBuild.js +0 -37
- package/lib/about/ui/AboutBuild.js.map +0 -1
- package/src/about/ui/AboutBuild.tsx +0 -54
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSProperties, ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
import { buildLabel } from '../../build/core/buildInfo.ts';
|
|
3
4
|
import { CreditsList } from '../../credits/ui/CreditsList.tsx';
|
|
4
5
|
import { Wordmark } from '../../ecosystem/ui/Wordmark.tsx';
|
|
5
6
|
import { SiteMark } from '../../ecosystem/ui/marks.tsx';
|
|
@@ -7,10 +8,11 @@ import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
|
7
8
|
import type { AboutContent } from '../core/about.ts';
|
|
8
9
|
import { resolveAbout } from '../core/about.ts';
|
|
9
10
|
|
|
10
|
-
import { AboutBuild } from './AboutBuild.tsx';
|
|
11
11
|
import { AboutCitations } from './AboutCitations.tsx';
|
|
12
12
|
import { AboutProvidedBy } from './AboutProvidedBy.tsx';
|
|
13
13
|
import { AboutSection } from './AboutSection.tsx';
|
|
14
|
+
import type { AboutSignInProps } from './AboutSignIn.tsx';
|
|
15
|
+
import { AboutSignIn } from './AboutSignIn.tsx';
|
|
14
16
|
import { AboutVersion } from './AboutVersion.tsx';
|
|
15
17
|
|
|
16
18
|
export interface AboutPageProps {
|
|
@@ -43,6 +45,13 @@ export interface AboutPageProps {
|
|
|
43
45
|
* @default undefined
|
|
44
46
|
*/
|
|
45
47
|
logo?: ReactNode;
|
|
48
|
+
/**
|
|
49
|
+
* Where the people who run the site sign in, drawn under the build badge in
|
|
50
|
+
* the corner of the hero. A site passes it while nobody is signed in, and
|
|
51
|
+
* leaves it out once somebody is: the account is then in the bar.
|
|
52
|
+
* @default undefined — the site has no account to sign in to
|
|
53
|
+
*/
|
|
54
|
+
signIn?: AboutSignInProps;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
/**
|
|
@@ -58,10 +67,12 @@ export interface AboutPageProps {
|
|
|
58
67
|
* @throws {Error} When the record names a site or a credit that does not exist.
|
|
59
68
|
*/
|
|
60
69
|
export function AboutPage(props: AboutPageProps): ReactElement {
|
|
61
|
-
const { content, className, children, logo, mark } = props;
|
|
70
|
+
const { content, className, children, logo, mark, signIn } = props;
|
|
62
71
|
const t = useChromeT();
|
|
63
72
|
const about = resolveAbout(content);
|
|
64
73
|
const site = about.site;
|
|
74
|
+
const hasCorner =
|
|
75
|
+
buildLabel(about.build) !== undefined || signIn !== undefined;
|
|
65
76
|
|
|
66
77
|
return (
|
|
67
78
|
<div
|
|
@@ -83,7 +94,12 @@ export function AboutPage(props: AboutPageProps): ReactElement {
|
|
|
83
94
|
</p>
|
|
84
95
|
<p style={WHAT_STYLE}>{about.what}</p>
|
|
85
96
|
</div>
|
|
86
|
-
|
|
97
|
+
{hasCorner ? (
|
|
98
|
+
<div className="about-corner" style={CORNER_STYLE}>
|
|
99
|
+
<AboutVersion build={about.build} />
|
|
100
|
+
{signIn === undefined ? null : <AboutSignIn {...signIn} />}
|
|
101
|
+
</div>
|
|
102
|
+
) : null}
|
|
87
103
|
</header>
|
|
88
104
|
|
|
89
105
|
{about.people.length === 0 && about.providedBy.length === 0 ? null : (
|
|
@@ -127,16 +143,6 @@ export function AboutPage(props: AboutPageProps): ReactElement {
|
|
|
127
143
|
<AboutCitations works={about.cite} />
|
|
128
144
|
</AboutSection>
|
|
129
145
|
)}
|
|
130
|
-
|
|
131
|
-
{/*
|
|
132
|
-
The build the reader has open, last and under no heading: it is what a
|
|
133
|
-
report has to name, not something anybody came to the page to read.
|
|
134
|
-
*/}
|
|
135
|
-
{about.build === undefined ? null : (
|
|
136
|
-
<AboutSection className="about-build">
|
|
137
|
-
<AboutBuild build={about.build} />
|
|
138
|
-
</AboutSection>
|
|
139
|
-
)}
|
|
140
146
|
</div>
|
|
141
147
|
);
|
|
142
148
|
}
|
|
@@ -166,6 +172,14 @@ const HERO_STYLE = {
|
|
|
166
172
|
lineHeight: 1.55,
|
|
167
173
|
} as const satisfies CSSProperties;
|
|
168
174
|
|
|
175
|
+
const CORNER_STYLE = {
|
|
176
|
+
display: 'flex',
|
|
177
|
+
flex: '0 0 auto',
|
|
178
|
+
flexDirection: 'column',
|
|
179
|
+
alignItems: 'flex-end',
|
|
180
|
+
gap: 2,
|
|
181
|
+
} as const satisfies CSSProperties;
|
|
182
|
+
|
|
169
183
|
const NAME_STYLE = {
|
|
170
184
|
margin: 0,
|
|
171
185
|
fontSize: 26,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isModifiedClick } from '../../chrome/ui/navItem.ts';
|
|
4
|
+
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
5
|
+
|
|
6
|
+
export interface AboutSignInProps {
|
|
7
|
+
/**
|
|
8
|
+
* The address signing in happens at. Writing it keeps the entry a real link,
|
|
9
|
+
* so a middle click opens a tab.
|
|
10
|
+
* @default undefined
|
|
11
|
+
*/
|
|
12
|
+
href?: string;
|
|
13
|
+
/**
|
|
14
|
+
* What the site does when the entry is picked — route to that page, or open
|
|
15
|
+
* a credentials dialog for a site that has no page. A modified click is left
|
|
16
|
+
* to the browser.
|
|
17
|
+
* @default undefined
|
|
18
|
+
*/
|
|
19
|
+
onSignIn?: () => void;
|
|
20
|
+
/**
|
|
21
|
+
* What the entry reads, for a site whose accounts are for one kind of person.
|
|
22
|
+
* @default the chrome's own word for it, in the language of the page
|
|
23
|
+
*/
|
|
24
|
+
label?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Where the people who run a site sign in: one quiet line under the build
|
|
29
|
+
* badge, in the corner of the About.
|
|
30
|
+
*
|
|
31
|
+
* A visitor has nothing to sign in to, so the bar carries no invitation; the
|
|
32
|
+
* About is the page the operators already know, and the corner is where they
|
|
33
|
+
* already look for the build they run.
|
|
34
|
+
* @param props - See {@link AboutSignInProps}.
|
|
35
|
+
* @returns The entry.
|
|
36
|
+
*/
|
|
37
|
+
export function AboutSignIn(props: AboutSignInProps): ReactElement {
|
|
38
|
+
const { href, onSignIn, label } = props;
|
|
39
|
+
const t = useChromeT();
|
|
40
|
+
const text = label ?? t('account.signIn');
|
|
41
|
+
|
|
42
|
+
if (href === undefined) {
|
|
43
|
+
return (
|
|
44
|
+
<button type="button" className="about-sign-in" onClick={onSignIn}>
|
|
45
|
+
{text}
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<a
|
|
52
|
+
className="about-sign-in"
|
|
53
|
+
href={href}
|
|
54
|
+
onClick={(event) => {
|
|
55
|
+
if (onSignIn === undefined || isModifiedClick(event)) return;
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
onSignIn();
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{text}
|
|
61
|
+
</a>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -4,18 +4,22 @@
|
|
|
4
4
|
* A report of something going wrong is worth answering only when we know what
|
|
5
5
|
* was running, and that is the one thing an About page cannot say from a
|
|
6
6
|
* hand-written record. It sits at the top of the page rather than at the
|
|
7
|
-
* bottom because it is the one line a reader is asked to quote back
|
|
7
|
+
* bottom because it is the one line a reader is asked to quote back, and it is
|
|
8
|
+
* said once: nothing further down the page repeats it.
|
|
8
9
|
*/
|
|
9
10
|
|
|
11
|
+
import { Tooltip } from '@blueprintjs/core';
|
|
10
12
|
import type { CSSProperties, ReactElement } from 'react';
|
|
11
13
|
|
|
12
14
|
import type { BuildInfo } from '../../build/core/buildInfo.ts';
|
|
13
15
|
import {
|
|
14
16
|
buildLabel,
|
|
15
17
|
buildStamp,
|
|
16
|
-
|
|
18
|
+
releasedVersion,
|
|
17
19
|
} from '../../build/core/buildInfo.ts';
|
|
18
20
|
|
|
21
|
+
import { AboutBuildDetails } from './AboutBuildDetails.tsx';
|
|
22
|
+
|
|
19
23
|
export interface AboutVersionProps {
|
|
20
24
|
/** What the build published about itself, or `undefined` when it published none. */
|
|
21
25
|
build: BuildInfo | undefined;
|
|
@@ -23,7 +27,8 @@ export interface AboutVersionProps {
|
|
|
23
27
|
|
|
24
28
|
/**
|
|
25
29
|
* The build badge: the release a site is serving, or the commit it was built
|
|
26
|
-
* from while it has no release yet, and when it was made.
|
|
30
|
+
* from while it has no release yet, and when it was made. A release names its
|
|
31
|
+
* commit and the exact instant in the card its hover opens.
|
|
27
32
|
* @param props - See {@link AboutVersionProps}.
|
|
28
33
|
* @returns The badge, or `null` when the build says nothing worth showing.
|
|
29
34
|
*/
|
|
@@ -34,15 +39,28 @@ export function AboutVersion(props: AboutVersionProps): ReactElement | null {
|
|
|
34
39
|
// The instant belongs on the badge rather than in its hover: a version
|
|
35
40
|
// nobody can date says nothing about how old the page they read is.
|
|
36
41
|
const badge = `${label} · ${buildStamp(build.builtAt)}`;
|
|
42
|
+
const version = releasedVersion(build);
|
|
43
|
+
|
|
44
|
+
if (version === undefined) {
|
|
45
|
+
return (
|
|
46
|
+
<span style={BADGE_STYLE} className="about-version">
|
|
47
|
+
{badge}
|
|
48
|
+
</span>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
37
51
|
|
|
38
52
|
return (
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
<Tooltip
|
|
54
|
+
content={<AboutBuildDetails build={build} version={version} />}
|
|
55
|
+
// To the left, so the card never covers the way to sign in under it.
|
|
56
|
+
placement="left-start"
|
|
57
|
+
popoverClassName="help-tooltip build-tooltip"
|
|
58
|
+
hoverOpenDelay={150}
|
|
43
59
|
>
|
|
44
|
-
{
|
|
45
|
-
|
|
60
|
+
<span style={BADGE_STYLE} className="about-version" tabIndex={0}>
|
|
61
|
+
{badge}
|
|
62
|
+
</span>
|
|
63
|
+
</Tooltip>
|
|
46
64
|
);
|
|
47
65
|
}
|
|
48
66
|
|
package/src/about/ui/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export type { AboutBuildProps } from './AboutBuild.tsx';
|
|
2
|
-
export { AboutBuild } from './AboutBuild.tsx';
|
|
3
1
|
export type { AboutVersionProps } from './AboutVersion.tsx';
|
|
4
2
|
export { AboutVersion } from './AboutVersion.tsx';
|
|
5
3
|
export type { AboutCitationsProps } from './AboutCitations.tsx';
|
|
@@ -10,3 +8,5 @@ export type { AboutProvidedByProps } from './AboutProvidedBy.tsx';
|
|
|
10
8
|
export { AboutProvidedBy } from './AboutProvidedBy.tsx';
|
|
11
9
|
export type { AboutSectionProps } from './AboutSection.tsx';
|
|
12
10
|
export { AboutSection } from './AboutSection.tsx';
|
|
11
|
+
export type { AboutSignInProps } from './AboutSignIn.tsx';
|
|
12
|
+
export { AboutSignIn } from './AboutSignIn.tsx';
|
|
@@ -180,9 +180,11 @@ const LAYER_STYLE = {
|
|
|
180
180
|
pointerEvents: 'none',
|
|
181
181
|
} as const satisfies CSSProperties;
|
|
182
182
|
|
|
183
|
+
// The slots run along x, so only a sideways finger tracks them: a vertical
|
|
184
|
+
// swipe that starts on the chart still scrolls the page or the card around it.
|
|
183
185
|
const SLIDER_STYLE = {
|
|
184
186
|
pointerEvents: 'auto',
|
|
185
|
-
touchAction: '
|
|
187
|
+
touchAction: 'pan-y',
|
|
186
188
|
cursor: 'crosshair',
|
|
187
189
|
} as const satisfies CSSProperties;
|
|
188
190
|
|
|
@@ -89,6 +89,34 @@ export function nextChartSize(
|
|
|
89
89
|
return { width: nextWidth, height: nextHeight };
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* The size an element is laid out at, which no CSS transform changes.
|
|
94
|
+
*
|
|
95
|
+
* `getBoundingClientRect` reports the box as painted: inside a popover that
|
|
96
|
+
* opens by scaling up from 30 %, a chart measured on its first frame is drawn
|
|
97
|
+
* at 30 % of its width, and stays so, since a transform never resizes the
|
|
98
|
+
* layout and the observer never fires again. `offsetWidth` is the laid-out
|
|
99
|
+
* size but rounded to a whole pixel, so the precise rectangle is kept unless a
|
|
100
|
+
* transform has visibly scaled it.
|
|
101
|
+
* @param element - The wrapper the chart is drawn in.
|
|
102
|
+
* @returns Its laid-out width and height.
|
|
103
|
+
*/
|
|
104
|
+
export function layoutSize(element: Element): {
|
|
105
|
+
width: number;
|
|
106
|
+
height: number;
|
|
107
|
+
} {
|
|
108
|
+
const rect = element.getBoundingClientRect();
|
|
109
|
+
if (!hasOffsetSize(element)) {
|
|
110
|
+
return { width: rect.width, height: rect.height };
|
|
111
|
+
}
|
|
112
|
+
const { offsetWidth, offsetHeight } = element;
|
|
113
|
+
return {
|
|
114
|
+
width: Math.abs(rect.width - offsetWidth) < 1 ? rect.width : offsetWidth,
|
|
115
|
+
height:
|
|
116
|
+
Math.abs(rect.height - offsetHeight) < 1 ? rect.height : offsetHeight,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
92
120
|
/** What a chart is given before its element has ever been laid out. */
|
|
93
121
|
const NO_SIZE: ChartSize = { width: 0, height: 0 };
|
|
94
122
|
|
|
@@ -101,10 +129,25 @@ function read(
|
|
|
101
129
|
element: Element,
|
|
102
130
|
setSize: Dispatch<SetStateAction<ChartSize>>,
|
|
103
131
|
): void {
|
|
104
|
-
const { width, height } = element
|
|
132
|
+
const { width, height } = layoutSize(element);
|
|
105
133
|
setSize((current) => nextChartSize(current, width, height));
|
|
106
134
|
}
|
|
107
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Whether an element has a laid-out size of its own, as an HTML element does
|
|
138
|
+
* and an SVG one does not. Checked by shape, so it holds outside a browser.
|
|
139
|
+
* @param element - The element.
|
|
140
|
+
* @returns True when `offsetWidth` and `offsetHeight` are numbers.
|
|
141
|
+
*/
|
|
142
|
+
function hasOffsetSize(
|
|
143
|
+
element: Element,
|
|
144
|
+
): element is Element & { offsetWidth: number; offsetHeight: number } {
|
|
145
|
+
return (
|
|
146
|
+
typeof (element as Partial<HTMLElement>).offsetWidth === 'number' &&
|
|
147
|
+
typeof (element as Partial<HTMLElement>).offsetHeight === 'number'
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
108
151
|
/**
|
|
109
152
|
* One side of a measurement, as a size rather than as a number.
|
|
110
153
|
* @param value - What the element was measured at.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
import { NavLink } from './NavLink.tsx';
|
|
4
|
+
import { NavMenuButton } from './NavMenuButton.tsx';
|
|
5
|
+
import type { NavItem } from './navItem.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* One page of the bar: a link, or the menu it opens when the entry folds pages
|
|
9
|
+
* of its own. A menu stands where its pages would, among them and not among the
|
|
10
|
+
* utilities, because picking one of them changes what the page shows.
|
|
11
|
+
* @param props - The entry and the page on show.
|
|
12
|
+
* @param props.item - The entry.
|
|
13
|
+
* @param props.activeId - The page on show.
|
|
14
|
+
* @returns The entry.
|
|
15
|
+
*/
|
|
16
|
+
export function NavEntry(props: {
|
|
17
|
+
item: NavItem;
|
|
18
|
+
activeId: string | undefined;
|
|
19
|
+
}): ReactElement {
|
|
20
|
+
const { item, activeId } = props;
|
|
21
|
+
if (item.items === undefined) {
|
|
22
|
+
return <NavLink item={item} active={item.id === activeId} />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<NavMenuButton
|
|
27
|
+
label={menuLabel(item)}
|
|
28
|
+
icon={typeof item.icon === 'string' ? item.icon : undefined}
|
|
29
|
+
items={item.items}
|
|
30
|
+
activeId={activeId}
|
|
31
|
+
placement="bottom-start"
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The trigger names itself to a screen reader, so an entry drawn as a glyph
|
|
37
|
+
// alone says what it opens through its title rather than through its id.
|
|
38
|
+
function menuLabel(item: NavItem): string {
|
|
39
|
+
if (typeof item.label === 'string') return item.label;
|
|
40
|
+
return item.title ?? item.id;
|
|
41
|
+
}
|
|
@@ -10,7 +10,7 @@ import { SiteMark } from '../../ecosystem/ui/marks.tsx';
|
|
|
10
10
|
import { useLanguage } from '../../i18n/ui/useLanguage.ts';
|
|
11
11
|
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { NavEntry } from './NavEntry.tsx';
|
|
14
14
|
import { NavMenuButton } from './NavMenuButton.tsx';
|
|
15
15
|
import type { NavItem } from './navItem.ts';
|
|
16
16
|
import { isModifiedClick } from './navItem.ts';
|
|
@@ -42,13 +42,20 @@ export interface SiteHeaderProps {
|
|
|
42
42
|
*/
|
|
43
43
|
activeId?: string;
|
|
44
44
|
/**
|
|
45
|
-
* The utilities pushed to the right edge — About, Cite, Tools, the language
|
|
46
|
-
*
|
|
47
|
-
* arrive dressed as bar items, so a plain `nav-link` and a `CiteButton`
|
|
48
|
-
* alike beside each other.
|
|
45
|
+
* The utilities pushed to the right edge — About, Cite, Tools, the language;
|
|
46
|
+
* Share and the account have props of their own, so they always come last.
|
|
47
|
+
* They arrive dressed as bar items, so a plain `nav-link` and a `CiteButton`
|
|
48
|
+
* read alike beside each other.
|
|
49
49
|
* @default undefined
|
|
50
50
|
*/
|
|
51
51
|
actions?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Who is signed in, drawn at the extreme right, after Share — usually an
|
|
54
|
+
* `AccountButton`. A site passes it only while somebody is signed in: signing
|
|
55
|
+
* in starts on the About (`AboutPage`'s `signIn`), never in the bar.
|
|
56
|
+
* @default undefined
|
|
57
|
+
*/
|
|
58
|
+
account?: ReactNode;
|
|
52
59
|
/**
|
|
53
60
|
* The Share entry, which closes the utilities on every site, after the
|
|
54
61
|
* Contact entry. A site passes its own when it wraps the shared `ShareButton`
|
|
@@ -133,6 +140,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
133
140
|
activeId,
|
|
134
141
|
actions,
|
|
135
142
|
share,
|
|
143
|
+
account,
|
|
136
144
|
contact,
|
|
137
145
|
renderNavItem,
|
|
138
146
|
embedded = false,
|
|
@@ -195,6 +203,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
195
203
|
<span className="spacer" />
|
|
196
204
|
{actions === undefined &&
|
|
197
205
|
share === undefined &&
|
|
206
|
+
account === undefined &&
|
|
198
207
|
contactForm === '' ? null : (
|
|
199
208
|
<div className="app-header-actions">
|
|
200
209
|
{actions}
|
|
@@ -202,6 +211,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
202
211
|
<ContactButton siteId={site.id} formUrl={contactForm} />
|
|
203
212
|
)}
|
|
204
213
|
{share}
|
|
214
|
+
{account}
|
|
205
215
|
</div>
|
|
206
216
|
)}
|
|
207
217
|
</div>
|
|
@@ -209,42 +219,6 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
209
219
|
);
|
|
210
220
|
}
|
|
211
221
|
|
|
212
|
-
/**
|
|
213
|
-
* One page of the bar: a link, or the menu it opens when the entry folds pages
|
|
214
|
-
* of its own. A menu stands where its pages would, among them and not among the
|
|
215
|
-
* utilities, because picking one of them changes what the page shows.
|
|
216
|
-
* @param props - The entry and the page on show.
|
|
217
|
-
* @param props.item - The entry.
|
|
218
|
-
* @param props.activeId - The page on show.
|
|
219
|
-
* @returns The entry.
|
|
220
|
-
*/
|
|
221
|
-
function NavEntry(props: {
|
|
222
|
-
item: NavItem;
|
|
223
|
-
activeId: string | undefined;
|
|
224
|
-
}): ReactElement {
|
|
225
|
-
const { item, activeId } = props;
|
|
226
|
-
if (item.items === undefined) {
|
|
227
|
-
return <NavLink item={item} active={item.id === activeId} />;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return (
|
|
231
|
-
<NavMenuButton
|
|
232
|
-
label={menuLabel(item)}
|
|
233
|
-
icon={typeof item.icon === 'string' ? item.icon : undefined}
|
|
234
|
-
items={item.items}
|
|
235
|
-
activeId={activeId}
|
|
236
|
-
placement="bottom-start"
|
|
237
|
-
/>
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// The trigger names itself to a screen reader, so an entry drawn as a glyph
|
|
242
|
-
// alone says what it opens through its title rather than through its id.
|
|
243
|
-
function menuLabel(item: NavItem): string {
|
|
244
|
-
if (typeof item.label === 'string') return item.label;
|
|
245
|
-
return item.title ?? item.id;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
222
|
/** The two widths the bar's contents run at, as the classes that set them. */
|
|
249
223
|
const INNER_CLASS = {
|
|
250
224
|
page: 'app-header__inner',
|
package/src/core.ts
CHANGED
|
@@ -14,6 +14,8 @@ export * from './error/core/index.ts';
|
|
|
14
14
|
export * from './format/core/index.ts';
|
|
15
15
|
export * from './i18n/core/index.ts';
|
|
16
16
|
export * from './language/core/index.ts';
|
|
17
|
+
export * from './moleculesSources/core/index.ts';
|
|
18
|
+
export * from './moleculesToken/core/index.ts';
|
|
17
19
|
export * from './number/core/index.ts';
|
|
18
20
|
export * from './orbital/core/index.ts';
|
|
19
21
|
export * from './overlay/core/index.ts';
|
|
@@ -52,4 +52,18 @@ export const UNLISTED_SITES: readonly EcosystemSite[] = [
|
|
|
52
52
|
// so the link row of the mark takes a lighter blue of the same hue.
|
|
53
53
|
mark: { plate: '#5c4033', accent: '#6b9bf2' },
|
|
54
54
|
},
|
|
55
|
+
{
|
|
56
|
+
id: 'mz',
|
|
57
|
+
name: { lead: 'mz', alt: 'cheminfo', dot: true },
|
|
58
|
+
host: 'mz.cheminfo.org',
|
|
59
|
+
tagline: 'Mass spectra, classified by how they were measured.',
|
|
60
|
+
group: 'spectra',
|
|
61
|
+
// Ultramarine, the deep blue of a dye whose colour is its spectrum.
|
|
62
|
+
brand: '#2200c4',
|
|
63
|
+
// An orange answers it at 5.2:1 on white, for the name.
|
|
64
|
+
brandAlt: '#c2410c',
|
|
65
|
+
// The mark takes a brighter orange, so the base peak stands apart from
|
|
66
|
+
// the ultramarine plate at 16 px.
|
|
67
|
+
mark: { plate: '#2200c4', accent: '#f97316' },
|
|
68
|
+
},
|
|
55
69
|
];
|
|
@@ -530,6 +530,16 @@ export const GLYPHS: Record<SiteId, (alt: string) => ReactNode> = {
|
|
|
530
530
|
/>
|
|
531
531
|
</>
|
|
532
532
|
),
|
|
533
|
+
// A mass spectrum: three peaks on a baseline, the tallest, the base peak, in
|
|
534
|
+
// the answering colour. Every edge falls on a whole pixel at 16 px.
|
|
535
|
+
mz: (alt) => (
|
|
536
|
+
<>
|
|
537
|
+
<rect x="4" y="26" width="24" height="2" fill="#ffffff" />
|
|
538
|
+
<rect x="6" y="16" width="4" height="10" rx="1" fill="#ffffff" />
|
|
539
|
+
<rect x="14" y="6" width="4" height="20" rx="1" fill={alt} />
|
|
540
|
+
<rect x="22" y="12" width="4" height="14" rx="1" fill="#ffffff" />
|
|
541
|
+
</>
|
|
542
|
+
),
|
|
533
543
|
// An infobox: the structure at its top, a ring, and two rows of data under
|
|
534
544
|
// it. The second row carries the answering colour, the blue of a link. Both
|
|
535
545
|
// rows and the gap between them fall on whole pixels at 16 px.
|
package/src/locales/de.json
CHANGED
|
@@ -107,10 +107,11 @@
|
|
|
107
107
|
"about.providedBy": "Bereitgestellt von",
|
|
108
108
|
"about.whatYouCanDo": "Was Sie hier tun können",
|
|
109
109
|
"about.builtOn": "Aufgebaut auf",
|
|
110
|
+
"about.buildVersion": "Version {version}",
|
|
111
|
+
"about.buildBuilt": "Erstellt",
|
|
112
|
+
"about.buildCommit": "Commit",
|
|
110
113
|
"about.howToCite": "Wie zu zitieren",
|
|
111
114
|
"about.citeWork": "{what} zitieren",
|
|
112
|
-
"about.built": "Erstellt {builtAt}.",
|
|
113
|
-
"about.builtFromCommit": "Erstellt {builtAt} aus Commit {commit}.",
|
|
114
115
|
"about.nameList": "{names} und {last}",
|
|
115
116
|
"help.label": "Hilfe",
|
|
116
117
|
"help.learnMore": "Mehr erfahren",
|
|
@@ -593,5 +594,37 @@
|
|
|
593
594
|
"taxonomy.rank.noRank": "ohne Rang",
|
|
594
595
|
"taxonomy.rank.clade": "Klade",
|
|
595
596
|
"taxonomy.rank.cellularRoot": "zelluläre Wurzel",
|
|
596
|
-
"taxonomy.rank.acellularRoot": "azelluläre Wurzel"
|
|
597
|
+
"taxonomy.rank.acellularRoot": "azelluläre Wurzel",
|
|
598
|
+
"moleculesToken.title": "Tokens für molecules.cheminfo.org",
|
|
599
|
+
"moleculesToken.checkedAt": "Geprüft am {when}",
|
|
600
|
+
"moleculesToken.checkAgain": "Erneut prüfen",
|
|
601
|
+
"moleculesToken.notChecked": "Noch nicht geprüft: Der Dienst, der in molecules.cheminfo.org schreibt, prüft seine Tokens beim Start.",
|
|
602
|
+
"moleculesToken.column.variable": "Variable",
|
|
603
|
+
"moleculesToken.column.source": "Quelle",
|
|
604
|
+
"moleculesToken.column.status": "Status",
|
|
605
|
+
"moleculesToken.column.details": "Details",
|
|
606
|
+
"moleculesToken.anySource": "beliebige Quelle",
|
|
607
|
+
"moleculesToken.verdict.ok": "gültig",
|
|
608
|
+
"moleculesToken.verdict.otherSource": "einer anderen Quelle",
|
|
609
|
+
"moleculesToken.verdict.refused": "abgelehnt",
|
|
610
|
+
"moleculesToken.verdict.unset": "nicht gesetzt",
|
|
611
|
+
"moleculesToken.verdict.uncheckable": "nicht prüfbar",
|
|
612
|
+
"moleculesToken.verdict.unanswered": "keine Antwort",
|
|
613
|
+
"moleculesToken.forSource": "für {source}",
|
|
614
|
+
"moleculesToken.issued": "ausgestellt am {when}",
|
|
615
|
+
"moleculesToken.issuedTo": "ausgestellt für {email} am {when}",
|
|
616
|
+
"moleculesToken.lastUsed": "zuletzt verwendet am {when}",
|
|
617
|
+
"moleculesToken.neverUsed": "nie verwendet",
|
|
618
|
+
"moleculesToken.otherSourceDetail": "ausgestellt für {source}: Was er sendet, wird dieser Quelle zugeschrieben",
|
|
619
|
+
"moleculesToken.refusedDetail": "nie ausgestellt oder widerrufen",
|
|
620
|
+
"moleculesToken.unsetDetail": "Setzen Sie {variable} in der .env des Servers.",
|
|
621
|
+
"moleculesToken.uncheckableDetail": "Dieses molecules.cheminfo.org kann noch keine Tokens prüfen.",
|
|
622
|
+
"moleculesToken.howToChange": "Ein Token wird in der .env des Servers geändert und beim Neustart des Dienstes neu gelesen. molecules.cheminfo.org stellt einen neuen aus mit",
|
|
623
|
+
"moleculesToken.fix.title": "Setzen Sie ein gültiges Token in {variables}",
|
|
624
|
+
"moleculesToken.fix.issueOne": "Stellen Sie ein Token auf der Admin-Seite von molecules.cheminfo.org aus:",
|
|
625
|
+
"moleculesToken.fix.issueMany": "Stellen Sie ein Token pro Quelle auf der Admin-Seite von molecules.cheminfo.org aus:",
|
|
626
|
+
"moleculesToken.fix.issueOnServer": "Oder auf dem Server von molecules.cheminfo.org:",
|
|
627
|
+
"moleculesToken.fix.envOne": "Tragen Sie es in die .env des Servers dieser Website ein:",
|
|
628
|
+
"moleculesToken.fix.envMany": "Tragen Sie sie in die .env des Servers dieser Website ein:",
|
|
629
|
+
"moleculesToken.fix.restart": "Starten Sie die Dienste neu, da sie die .env nur beim Start lesen:"
|
|
597
630
|
}
|
package/src/locales/en.json
CHANGED
|
@@ -107,10 +107,11 @@
|
|
|
107
107
|
"about.providedBy": "Provided by",
|
|
108
108
|
"about.whatYouCanDo": "What you can do here",
|
|
109
109
|
"about.builtOn": "Built on",
|
|
110
|
+
"about.buildVersion": "Version {version}",
|
|
111
|
+
"about.buildBuilt": "Built",
|
|
112
|
+
"about.buildCommit": "Commit",
|
|
110
113
|
"about.howToCite": "How to cite",
|
|
111
114
|
"about.citeWork": "Cite {what}",
|
|
112
|
-
"about.built": "Built {builtAt}.",
|
|
113
|
-
"about.builtFromCommit": "Built {builtAt} from commit {commit}.",
|
|
114
115
|
"about.nameList": "{names} and {last}",
|
|
115
116
|
"credits.openbabel.description": "reads and writes the chemical file formats, and does the conversion itself.",
|
|
116
117
|
"credits.openchemlib.description": "reads and writes structures, and computes their properties in the browser.",
|
|
@@ -593,5 +594,37 @@
|
|
|
593
594
|
"taxonomy.rank.noRank": "no rank",
|
|
594
595
|
"taxonomy.rank.clade": "clade",
|
|
595
596
|
"taxonomy.rank.cellularRoot": "cellular root",
|
|
596
|
-
"taxonomy.rank.acellularRoot": "acellular root"
|
|
597
|
+
"taxonomy.rank.acellularRoot": "acellular root",
|
|
598
|
+
"moleculesToken.title": "Tokens for molecules.cheminfo.org",
|
|
599
|
+
"moleculesToken.checkedAt": "Checked {when}",
|
|
600
|
+
"moleculesToken.checkAgain": "Check again",
|
|
601
|
+
"moleculesToken.notChecked": "Not checked yet: the service that writes to molecules.cheminfo.org checks its tokens when it starts.",
|
|
602
|
+
"moleculesToken.column.variable": "Variable",
|
|
603
|
+
"moleculesToken.column.source": "Source",
|
|
604
|
+
"moleculesToken.column.status": "Status",
|
|
605
|
+
"moleculesToken.column.details": "Details",
|
|
606
|
+
"moleculesToken.anySource": "any source",
|
|
607
|
+
"moleculesToken.verdict.ok": "valid",
|
|
608
|
+
"moleculesToken.verdict.otherSource": "another source's",
|
|
609
|
+
"moleculesToken.verdict.refused": "refused",
|
|
610
|
+
"moleculesToken.verdict.unset": "not set",
|
|
611
|
+
"moleculesToken.verdict.uncheckable": "cannot be checked",
|
|
612
|
+
"moleculesToken.verdict.unanswered": "no answer",
|
|
613
|
+
"moleculesToken.forSource": "for {source}",
|
|
614
|
+
"moleculesToken.issued": "issued on {when}",
|
|
615
|
+
"moleculesToken.issuedTo": "issued to {email} on {when}",
|
|
616
|
+
"moleculesToken.lastUsed": "last used on {when}",
|
|
617
|
+
"moleculesToken.neverUsed": "never used",
|
|
618
|
+
"moleculesToken.otherSourceDetail": "issued for {source}: what it sends is credited to that source",
|
|
619
|
+
"moleculesToken.refusedDetail": "never issued, or revoked",
|
|
620
|
+
"moleculesToken.unsetDetail": "Set {variable} in the server's .env.",
|
|
621
|
+
"moleculesToken.uncheckableDetail": "This molecules.cheminfo.org cannot check a token yet.",
|
|
622
|
+
"moleculesToken.howToChange": "A token is changed in the server's .env and read again when the service restarts. molecules.cheminfo.org issues a new one with",
|
|
623
|
+
"moleculesToken.fix.title": "Set a valid token in {variables}",
|
|
624
|
+
"moleculesToken.fix.issueOne": "Issue a token on the Admin page of molecules.cheminfo.org:",
|
|
625
|
+
"moleculesToken.fix.issueMany": "Issue one token per source on the Admin page of molecules.cheminfo.org:",
|
|
626
|
+
"moleculesToken.fix.issueOnServer": "Or, on the server of molecules.cheminfo.org:",
|
|
627
|
+
"moleculesToken.fix.envOne": "Write it in the .env of this site's server:",
|
|
628
|
+
"moleculesToken.fix.envMany": "Write them in the .env of this site's server:",
|
|
629
|
+
"moleculesToken.fix.restart": "Restart the services, which read the .env only when they start:"
|
|
597
630
|
}
|