react-cheminfo 0.57.0 → 0.59.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/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/core.d.ts +1 -0
- package/lib/core.d.ts.map +1 -1
- package/lib/core.js +1 -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 +19 -3
- 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 +4 -1
- 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 +26 -1
- package/lib/locales/en.json +26 -1
- package/lib/locales/es.json +26 -1
- package/lib/locales/fr.json +26 -1
- package/lib/locales/it.json +26 -1
- package/lib/moleculesToken/core/index.d.ts +3 -0
- package/lib/moleculesToken/core/index.d.ts.map +1 -0
- package/lib/moleculesToken/core/index.js +2 -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/ui/MoleculesTokenTable.d.ts +32 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.d.ts.map +1 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.js +124 -0
- package/lib/moleculesToken/ui/MoleculesTokenTable.js.map +1 -0
- package/lib/moleculesToken/ui/index.d.ts +3 -0
- package/lib/moleculesToken/ui/index.d.ts.map +1 -0
- package/lib/moleculesToken/ui/index.js +2 -0
- package/lib/moleculesToken/ui/index.js.map +1 -0
- package/lib/ui.d.ts +1 -0
- package/lib/ui.d.ts.map +1 -1
- package/lib/ui.js +1 -0
- package/lib/ui.js.map +1 -1
- package/package.json +1 -1
- package/src/chart/ui/ChartTrackingLayer.tsx +3 -1
- package/src/chart/ui/useChartSize.ts +44 -1
- package/src/core.ts +1 -0
- package/src/ecosystem/core/types.ts +3 -2
- package/src/ecosystem/core/unlistedSites.ts +19 -3
- package/src/ecosystem/ui/glyphs.tsx +11 -1
- package/src/locales/de.json +26 -1
- package/src/locales/en.json +26 -1
- package/src/locales/es.json +26 -1
- package/src/locales/fr.json +26 -1
- package/src/locales/it.json +26 -1
- package/src/moleculesToken/core/index.ts +11 -0
- package/src/moleculesToken/core/moleculesToken.ts +256 -0
- package/src/moleculesToken/ui/MoleculesTokenTable.tsx +249 -0
- package/src/moleculesToken/ui/index.ts +2 -0
- package/src/ui.ts +1 -0
- package/styles/chrome.css +35 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import type { Intent } from '@blueprintjs/core';
|
|
2
|
+
import { Button, Callout, HTMLTable, Tag } from '@blueprintjs/core';
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
import type { ChromeKey } from '../../i18n/core/chromeCatalog.ts';
|
|
7
|
+
import { useLanguage } from '../../i18n/ui/useLanguage.ts';
|
|
8
|
+
import type { Translate } from '../../i18n/ui/useT.ts';
|
|
9
|
+
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
10
|
+
import type {
|
|
11
|
+
MoleculesTokenCheck,
|
|
12
|
+
MoleculesTokenVerdict,
|
|
13
|
+
} from '../core/moleculesToken.ts';
|
|
14
|
+
|
|
15
|
+
/** What {@link MoleculesTokenTable} shows. */
|
|
16
|
+
export interface MoleculesTokenTableProps {
|
|
17
|
+
/** The last checks, or null before the first one was made. */
|
|
18
|
+
checks: readonly MoleculesTokenCheck[] | null;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a check was asked for and is not made yet.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
pending?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Asks for every token to be checked again. Left out, there is no button:
|
|
26
|
+
* a page anyone can open shows the last checks and asks for none.
|
|
27
|
+
* @default undefined
|
|
28
|
+
*/
|
|
29
|
+
onCheckAgain?: () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Why asking for a check failed.
|
|
32
|
+
* @default undefined
|
|
33
|
+
*/
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const INTENT: Record<MoleculesTokenVerdict, Intent> = {
|
|
38
|
+
ok: 'success',
|
|
39
|
+
otherSource: 'danger',
|
|
40
|
+
refused: 'danger',
|
|
41
|
+
unset: 'warning',
|
|
42
|
+
uncheckable: 'warning',
|
|
43
|
+
unanswered: 'warning',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** How each verdict reads in the table. */
|
|
47
|
+
const LABEL: Record<MoleculesTokenVerdict, ChromeKey> = {
|
|
48
|
+
ok: 'moleculesToken.verdict.ok',
|
|
49
|
+
otherSource: 'moleculesToken.verdict.otherSource',
|
|
50
|
+
refused: 'moleculesToken.verdict.refused',
|
|
51
|
+
unset: 'moleculesToken.verdict.unset',
|
|
52
|
+
uncheckable: 'moleculesToken.verdict.uncheckable',
|
|
53
|
+
unanswered: 'moleculesToken.verdict.unanswered',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** The command a token is issued with, on molecules.cheminfo.org. */
|
|
57
|
+
const ISSUE_COMMAND = 'npm run tokens -- issue SOURCE EMAIL';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The tokens a site holds for molecules.cheminfo.org, one row each, with what
|
|
61
|
+
* molecules.cheminfo.org said of it when it was last checked: valid, another
|
|
62
|
+
* source's, refused, not set. A token is changed in the server's `.env`, never
|
|
63
|
+
* here, so the table says where.
|
|
64
|
+
* @param props - See {@link MoleculesTokenTableProps}.
|
|
65
|
+
* @returns The table.
|
|
66
|
+
*/
|
|
67
|
+
export function MoleculesTokenTable(props: MoleculesTokenTableProps) {
|
|
68
|
+
const { checks, pending = false, onCheckAgain, error } = props;
|
|
69
|
+
const t = useChromeT();
|
|
70
|
+
const language = useLanguage();
|
|
71
|
+
const formats = useMemo(
|
|
72
|
+
() => ({
|
|
73
|
+
instant: new Intl.DateTimeFormat(language, {
|
|
74
|
+
dateStyle: 'medium',
|
|
75
|
+
timeStyle: 'short',
|
|
76
|
+
}),
|
|
77
|
+
sources: new Intl.ListFormat(language, { type: 'disjunction' }),
|
|
78
|
+
}),
|
|
79
|
+
[language],
|
|
80
|
+
);
|
|
81
|
+
const checkedAt =
|
|
82
|
+
checks === null || checks.length === 0
|
|
83
|
+
? null
|
|
84
|
+
: Math.min(...checks.map((check) => check.checkedAt));
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<section
|
|
88
|
+
className="molecules-tokens"
|
|
89
|
+
aria-label={t('moleculesToken.title')}
|
|
90
|
+
data-testid="molecules-tokens"
|
|
91
|
+
>
|
|
92
|
+
<div className="molecules-tokens__head">
|
|
93
|
+
<span className="molecules-tokens__title">
|
|
94
|
+
{t('moleculesToken.title')}
|
|
95
|
+
</span>
|
|
96
|
+
{checkedAt !== null && (
|
|
97
|
+
<span className="molecules-tokens__muted">
|
|
98
|
+
{t('moleculesToken.checkedAt', {
|
|
99
|
+
when: formats.instant.format(checkedAt),
|
|
100
|
+
})}
|
|
101
|
+
</span>
|
|
102
|
+
)}
|
|
103
|
+
{onCheckAgain !== undefined && (
|
|
104
|
+
<Button
|
|
105
|
+
size="small"
|
|
106
|
+
variant="minimal"
|
|
107
|
+
icon="refresh"
|
|
108
|
+
loading={pending}
|
|
109
|
+
text={t('moleculesToken.checkAgain')}
|
|
110
|
+
onClick={onCheckAgain}
|
|
111
|
+
data-testid="molecules-tokens-check"
|
|
112
|
+
/>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
{error !== undefined && (
|
|
116
|
+
<Callout compact intent="danger">
|
|
117
|
+
{error}
|
|
118
|
+
</Callout>
|
|
119
|
+
)}
|
|
120
|
+
{checks === null ? (
|
|
121
|
+
<p className="molecules-tokens__muted">
|
|
122
|
+
{t('moleculesToken.notChecked')}
|
|
123
|
+
</p>
|
|
124
|
+
) : (
|
|
125
|
+
<div className="molecules-tokens__scroll">
|
|
126
|
+
<HTMLTable compact striped className="molecules-tokens__table">
|
|
127
|
+
<thead>
|
|
128
|
+
<tr>
|
|
129
|
+
<th>{t('moleculesToken.column.variable')}</th>
|
|
130
|
+
<th>{t('moleculesToken.column.source')}</th>
|
|
131
|
+
<th>{t('moleculesToken.column.status')}</th>
|
|
132
|
+
<th>{t('moleculesToken.column.details')}</th>
|
|
133
|
+
</tr>
|
|
134
|
+
</thead>
|
|
135
|
+
<tbody>
|
|
136
|
+
{checks.map((check) => (
|
|
137
|
+
<tr key={check.variable} data-verdict={check.verdict}>
|
|
138
|
+
<td>
|
|
139
|
+
<code>{check.variable}</code>
|
|
140
|
+
</td>
|
|
141
|
+
<td>
|
|
142
|
+
{check.sources.length === 0
|
|
143
|
+
? t('moleculesToken.anySource')
|
|
144
|
+
: formats.sources.format(check.sources)}
|
|
145
|
+
</td>
|
|
146
|
+
<td>
|
|
147
|
+
<Tag minimal intent={INTENT[check.verdict]}>
|
|
148
|
+
{t(LABEL[check.verdict])}
|
|
149
|
+
</Tag>
|
|
150
|
+
</td>
|
|
151
|
+
<td>{details(check, t, formats.instant)}</td>
|
|
152
|
+
</tr>
|
|
153
|
+
))}
|
|
154
|
+
</tbody>
|
|
155
|
+
</HTMLTable>
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
<p className="molecules-tokens__muted">
|
|
159
|
+
{t('moleculesToken.howToChange')} <code>{ISSUE_COMMAND}</code>
|
|
160
|
+
</p>
|
|
161
|
+
</section>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* What a row says besides its verdict.
|
|
167
|
+
* @param check - The check.
|
|
168
|
+
* @param t - The chrome's translator.
|
|
169
|
+
* @param instant - How an instant is written.
|
|
170
|
+
* @returns The cell's content.
|
|
171
|
+
*/
|
|
172
|
+
function details(
|
|
173
|
+
check: MoleculesTokenCheck,
|
|
174
|
+
t: Translate<ChromeKey>,
|
|
175
|
+
instant: Intl.DateTimeFormat,
|
|
176
|
+
): ReactNode {
|
|
177
|
+
const hint: Fact | null =
|
|
178
|
+
check.hint === null
|
|
179
|
+
? null
|
|
180
|
+
: ['hint', <code key="hint">{check.hint}…</code>];
|
|
181
|
+
switch (check.verdict) {
|
|
182
|
+
case 'ok':
|
|
183
|
+
return facts([
|
|
184
|
+
check.sources.length === 1 || check.source === null
|
|
185
|
+
? null
|
|
186
|
+
: ['source', t('moleculesToken.forSource', { source: check.source })],
|
|
187
|
+
check.issuedAt === null
|
|
188
|
+
? null
|
|
189
|
+
: [
|
|
190
|
+
'issued',
|
|
191
|
+
check.email === null
|
|
192
|
+
? t('moleculesToken.issued', {
|
|
193
|
+
when: instant.format(check.issuedAt),
|
|
194
|
+
})
|
|
195
|
+
: t('moleculesToken.issuedTo', {
|
|
196
|
+
email: check.email,
|
|
197
|
+
when: instant.format(check.issuedAt),
|
|
198
|
+
}),
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
'used',
|
|
202
|
+
check.lastUsedAt === null
|
|
203
|
+
? t('moleculesToken.neverUsed')
|
|
204
|
+
: t('moleculesToken.lastUsed', {
|
|
205
|
+
when: instant.format(check.lastUsedAt),
|
|
206
|
+
}),
|
|
207
|
+
],
|
|
208
|
+
hint,
|
|
209
|
+
]);
|
|
210
|
+
case 'otherSource':
|
|
211
|
+
return facts([
|
|
212
|
+
[
|
|
213
|
+
'source',
|
|
214
|
+
t('moleculesToken.otherSourceDetail', {
|
|
215
|
+
source: check.source ?? '',
|
|
216
|
+
}),
|
|
217
|
+
],
|
|
218
|
+
hint,
|
|
219
|
+
]);
|
|
220
|
+
case 'refused':
|
|
221
|
+
return check.message ?? t('moleculesToken.refusedDetail');
|
|
222
|
+
case 'unset':
|
|
223
|
+
return t('moleculesToken.unsetDetail', { variable: check.variable });
|
|
224
|
+
case 'uncheckable':
|
|
225
|
+
return t('moleculesToken.uncheckableDetail');
|
|
226
|
+
case 'unanswered':
|
|
227
|
+
return check.message;
|
|
228
|
+
default:
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** One fact about a token, and the key it is drawn under. */
|
|
234
|
+
type Fact = readonly [key: string, content: ReactNode];
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Facts about a token, one after the other.
|
|
238
|
+
* @param parts - The facts; null ones are left out.
|
|
239
|
+
* @returns The facts, separated by a middle dot.
|
|
240
|
+
*/
|
|
241
|
+
function facts(parts: ReadonlyArray<Fact | null>): ReactNode {
|
|
242
|
+
const shown = parts.filter((part) => part !== null);
|
|
243
|
+
return shown.map(([key, content], index) => (
|
|
244
|
+
<span key={key}>
|
|
245
|
+
{index > 0 && ' · '}
|
|
246
|
+
{content}
|
|
247
|
+
</span>
|
|
248
|
+
));
|
|
249
|
+
}
|
package/src/ui.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from './help/ui/index.ts';
|
|
|
18
18
|
export * from './hooks/ui/index.ts';
|
|
19
19
|
export * from './i18n/ui/index.ts';
|
|
20
20
|
export * from './language/ui/index.ts';
|
|
21
|
+
export * from './moleculesToken/ui/index.ts';
|
|
21
22
|
export * from './number/ui/index.ts';
|
|
22
23
|
export * from './overlay/ui/index.ts';
|
|
23
24
|
export * from './panel/ui/index.ts';
|
package/styles/chrome.css
CHANGED
|
@@ -737,6 +737,41 @@ button {
|
|
|
737
737
|
}
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
/* ── Tokens for molecules.cheminfo.org ─────────────────────────────────────── */
|
|
741
|
+
|
|
742
|
+
/* The heading, when the tokens were checked, and the button asking again, on
|
|
743
|
+
one line that wraps on a phone. */
|
|
744
|
+
.molecules-tokens__head {
|
|
745
|
+
display: flex;
|
|
746
|
+
flex-wrap: wrap;
|
|
747
|
+
align-items: center;
|
|
748
|
+
margin-bottom: 0.4rem;
|
|
749
|
+
gap: 0.25rem 0.75rem;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.molecules-tokens__title {
|
|
753
|
+
font-weight: 600;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.molecules-tokens__muted {
|
|
757
|
+
color: var(--text-muted);
|
|
758
|
+
font-size: 0.8125rem;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/* A phone scrolls the table within its box, never the page sideways. */
|
|
762
|
+
.molecules-tokens__scroll {
|
|
763
|
+
overflow-x: auto;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.molecules-tokens__table {
|
|
767
|
+
width: 100%;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/* A variable name breaks nowhere: it is copied into a .env as it reads. */
|
|
771
|
+
.molecules-tokens__table code {
|
|
772
|
+
white-space: nowrap;
|
|
773
|
+
}
|
|
774
|
+
|
|
740
775
|
/* ── A narrow bar ────────────────────────────────────────────────────────── */
|
|
741
776
|
|
|
742
777
|
/* The menu the pages fold into, drawn only once they no longer fit. */
|