react-cheminfo 0.53.0 → 0.55.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 +14 -0
- package/lib/chrome/ui/SiteHeader.d.ts +17 -1
- package/lib/chrome/ui/SiteHeader.d.ts.map +1 -1
- package/lib/chrome/ui/SiteHeader.js +9 -2
- package/lib/chrome/ui/SiteHeader.js.map +1 -1
- package/lib/contact/core/contactUrl.d.ts +35 -0
- package/lib/contact/core/contactUrl.d.ts.map +1 -0
- package/lib/contact/core/contactUrl.js +33 -0
- package/lib/contact/core/contactUrl.js.map +1 -0
- package/lib/contact/core/index.d.ts +3 -0
- package/lib/contact/core/index.d.ts.map +1 -0
- package/lib/contact/core/index.js +2 -0
- package/lib/contact/core/index.js.map +1 -0
- package/lib/contact/ui/ContactButton.d.ts +41 -0
- package/lib/contact/ui/ContactButton.d.ts.map +1 -0
- package/lib/contact/ui/ContactButton.js +29 -0
- package/lib/contact/ui/ContactButton.js.map +1 -0
- package/lib/contact/ui/index.d.ts +3 -0
- package/lib/contact/ui/index.d.ts.map +1 -0
- package/lib/contact/ui/index.js +2 -0
- package/lib/contact/ui/index.js.map +1 -0
- 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/i18n/core/chromeCatalog.d.ts +1 -1
- package/lib/i18n/core/chromeCatalog.d.ts.map +1 -1
- package/lib/i18n/core/index.d.ts +2 -0
- package/lib/i18n/core/index.d.ts.map +1 -1
- package/lib/i18n/core/index.js +1 -0
- package/lib/i18n/core/index.js.map +1 -1
- package/lib/i18n/core/messageCatalog.d.ts +10 -0
- package/lib/i18n/core/messageCatalog.d.ts.map +1 -1
- package/lib/i18n/core/messageCatalog.js +12 -0
- package/lib/i18n/core/messageCatalog.js.map +1 -1
- package/lib/i18n/core/translate.d.ts +35 -0
- package/lib/i18n/core/translate.d.ts.map +1 -0
- package/lib/i18n/core/translate.js +27 -0
- package/lib/i18n/core/translate.js.map +1 -0
- package/lib/i18n/ui/useT.d.ts +2 -12
- package/lib/i18n/ui/useT.d.ts.map +1 -1
- package/lib/i18n/ui/useT.js +2 -8
- package/lib/i18n/ui/useT.js.map +1 -1
- package/lib/i18n/vite/index.d.ts +3 -0
- package/lib/i18n/vite/index.d.ts.map +1 -0
- package/lib/i18n/vite/index.js +2 -0
- package/lib/i18n/vite/index.js.map +1 -0
- package/lib/i18n/vite/localeCatalog.d.ts +70 -0
- package/lib/i18n/vite/localeCatalog.d.ts.map +1 -0
- package/lib/i18n/vite/localeCatalog.js +80 -0
- package/lib/i18n/vite/localeCatalog.js.map +1 -0
- package/lib/locales/de.json +2 -0
- package/lib/locales/en.json +2 -0
- package/lib/locales/es.json +2 -0
- package/lib/locales/fr.json +2 -0
- package/lib/locales/it.json +2 -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/lib/vite.d.ts +1 -0
- package/lib/vite.d.ts.map +1 -1
- package/lib/vite.js +1 -0
- package/lib/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/chrome/ui/SiteHeader.tsx +35 -3
- package/src/contact/core/contactUrl.ts +53 -0
- package/src/contact/core/index.ts +2 -0
- package/src/contact/ui/ContactButton.tsx +74 -0
- package/src/contact/ui/index.ts +2 -0
- package/src/core.ts +1 -0
- package/src/i18n/core/index.ts +2 -0
- package/src/i18n/core/messageCatalog.ts +13 -0
- package/src/i18n/core/translate.ts +51 -0
- package/src/i18n/ui/useT.ts +6 -29
- package/src/i18n/vite/index.ts +5 -0
- package/src/i18n/vite/localeCatalog.ts +121 -0
- package/src/locales/de.json +2 -0
- package/src/locales/en.json +2 -0
- package/src/locales/es.json +2 -0
- package/src/locales/fr.json +2 -0
- package/src/locales/it.json +2 -0
- package/src/ui.ts +1 -0
- package/src/vite.ts +1 -0
- package/styles/chrome.css +21 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The formatter a catalog's text is read through, outside React.
|
|
3
|
+
*
|
|
4
|
+
* `useT` is this function with the page's language and a redraw wired to it;
|
|
5
|
+
* the build and a site's own tests need the same shape without either, so it
|
|
6
|
+
* lives here rather than in the hook. A site never writes its own: four of
|
|
7
|
+
* them had, each with a slightly different fallback.
|
|
8
|
+
*/
|
|
9
|
+
import type { Language } from './languages.ts';
|
|
10
|
+
import type { LooseKey, MessageCatalog } from './messageCatalog.ts';
|
|
11
|
+
import type { MessageValues } from './messageValues.ts';
|
|
12
|
+
/**
|
|
13
|
+
* What text is read with.
|
|
14
|
+
*
|
|
15
|
+
* `t(key)` is a key the catalog declares, checked at compile time.
|
|
16
|
+
* `t.or(key, fallback)` is a key built from the data — a site's id, an
|
|
17
|
+
* element's symbol — which the catalog may not have caught up with yet; it
|
|
18
|
+
* reads as the fallback rather than as a raw key.
|
|
19
|
+
*/
|
|
20
|
+
export type Translate<TKey extends string> = ((key: LooseKey<TKey>, values?: MessageValues) => string) & {
|
|
21
|
+
or: (key: string, fallback: string, values?: MessageValues) => string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A catalog's formatter, in one language.
|
|
25
|
+
*
|
|
26
|
+
* In a component this is `useT`, which also redraws when the language or a
|
|
27
|
+
* translator's edit changes it. Here it is for the places a hook cannot go:
|
|
28
|
+
* the build writing a head per address per language, a test asserting on the
|
|
29
|
+
* text a page shows, a label computed in a module helper.
|
|
30
|
+
* @param catalog - The catalog to read, typed by its English keys.
|
|
31
|
+
* @param language - The language to read it in.
|
|
32
|
+
* @returns See {@link Translate}.
|
|
33
|
+
*/
|
|
34
|
+
export declare function catalogTranslate<TKey extends string>(catalog: MessageCatalog<TKey>, language: Language): Translate<TKey>;
|
|
35
|
+
//# sourceMappingURL=translate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../../src/i18n/core/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,CAAC,IAAI,SAAS,MAAM,IAAI,CAAC,CAC5C,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EACnB,MAAM,CAAC,EAAE,aAAa,KACnB,MAAM,CAAC,GAAG;IACb,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,KAAK,MAAM,CAAC;CACvE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAClD,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAC7B,QAAQ,EAAE,QAAQ,GACjB,SAAS,CAAC,IAAI,CAAC,CAQjB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The formatter a catalog's text is read through, outside React.
|
|
3
|
+
*
|
|
4
|
+
* `useT` is this function with the page's language and a redraw wired to it;
|
|
5
|
+
* the build and a site's own tests need the same shape without either, so it
|
|
6
|
+
* lives here rather than in the hook. A site never writes its own: four of
|
|
7
|
+
* them had, each with a slightly different fallback.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A catalog's formatter, in one language.
|
|
11
|
+
*
|
|
12
|
+
* In a component this is `useT`, which also redraws when the language or a
|
|
13
|
+
* translator's edit changes it. Here it is for the places a hook cannot go:
|
|
14
|
+
* the build writing a head per address per language, a test asserting on the
|
|
15
|
+
* text a page shows, a label computed in a module helper.
|
|
16
|
+
* @param catalog - The catalog to read, typed by its English keys.
|
|
17
|
+
* @param language - The language to read it in.
|
|
18
|
+
* @returns See {@link Translate}.
|
|
19
|
+
*/
|
|
20
|
+
export function catalogTranslate(catalog, language) {
|
|
21
|
+
// `or` is a property of the function itself, so the two are built together
|
|
22
|
+
// rather than assigned onto a value a render may already have read.
|
|
23
|
+
const translate = (key, values) => catalog.translate(key, language, { values });
|
|
24
|
+
translate.or = (key, fallback, values) => catalog.translate(key, language, { fallback, values });
|
|
25
|
+
return translate;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../../src/i18n/core/translate.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAqBH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAA6B,EAC7B,QAAkB;IAElB,2EAA2E;IAC3E,oEAAoE;IACpE,MAAM,SAAS,GAAG,CAAC,GAAmB,EAAE,MAAsB,EAAE,EAAE,CAChE,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/C,SAAS,CAAC,EAAE,GAAG,CAAC,GAAW,EAAE,QAAgB,EAAE,MAAsB,EAAE,EAAE,CACvE,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
package/lib/i18n/ui/useT.d.ts
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import type { ChromeKey } from '../core/chromeCatalog.ts';
|
|
2
|
-
import type { Language, LooseKey,
|
|
2
|
+
import type { Language, LooseKey, Translate, TranslateOptions } from '../core/index.ts';
|
|
3
3
|
import type { MessageCatalog } from '../core/messageCatalog.ts';
|
|
4
|
-
|
|
5
|
-
* What a component reads its text with.
|
|
6
|
-
*
|
|
7
|
-
* `t(key)` is a key the catalog declares, checked at compile time.
|
|
8
|
-
* `t.or(key, fallback)` is a key built from the data — a site's id, an
|
|
9
|
-
* element's symbol — which the catalog may not have caught up with yet; it
|
|
10
|
-
* reads as the fallback rather than as a raw key.
|
|
11
|
-
*/
|
|
12
|
-
export type Translate<TKey extends string> = ((key: LooseKey<TKey>, values?: MessageValues) => string) & {
|
|
13
|
-
or: (key: string, fallback: string, values?: MessageValues) => string;
|
|
14
|
-
};
|
|
4
|
+
export type { Translate } from '../core/index.ts';
|
|
15
5
|
/**
|
|
16
6
|
* The formatter of a catalog's text, in the language the page is written in.
|
|
17
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useT.d.ts","sourceRoot":"","sources":["../../../src/i18n/ui/useT.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,
|
|
1
|
+
{"version":3,"file":"useT.d.ts","sourceRoot":"","sources":["../../../src/i18n/ui/useT.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAMhE,YAAY,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,IAAI,SAAS,MAAM,EACtC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,GAC5B,SAAS,CAAC,IAAI,CAAC,CAKjB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,IAAI,SAAS,CAAC,SAAS,CAAC,CAEjD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAClD,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,EAC7B,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,EACnB,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CAER"}
|
package/lib/i18n/ui/useT.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CHROME_CATALOG } from "../core/chromeCatalog.js";
|
|
2
|
+
import { catalogTranslate } from "../core/index.js";
|
|
2
3
|
import { useLanguage, useMessagesVersion } from "./useLanguage.js";
|
|
3
4
|
/**
|
|
4
5
|
* The formatter of a catalog's text, in the language the page is written in.
|
|
@@ -13,14 +14,7 @@ export function useT(catalog) {
|
|
|
13
14
|
const language = useLanguage();
|
|
14
15
|
useMessagesVersion();
|
|
15
16
|
catalog.prefetch(language);
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
// Built outside the hook, because the `or` form is a property of the function
|
|
19
|
-
// itself and a render may not write to a value it has just read.
|
|
20
|
-
function translator(catalog, language) {
|
|
21
|
-
const translate = (key, values) => catalog.translate(key, language, { values });
|
|
22
|
-
translate.or = (key, fallback, values) => catalog.translate(key, language, { fallback, values });
|
|
23
|
-
return translate;
|
|
17
|
+
return catalogTranslate(catalog, language);
|
|
24
18
|
}
|
|
25
19
|
/**
|
|
26
20
|
* The formatter of this library's own text — the header, the Tools menu, the
|
package/lib/i18n/ui/useT.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useT.js","sourceRoot":"","sources":["../../../src/i18n/ui/useT.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useT.js","sourceRoot":"","sources":["../../../src/i18n/ui/useT.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAO1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGpD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAMnE;;;;;;;;GAQG;AACH,MAAM,UAAU,IAAI,CAClB,OAA6B;IAE7B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,kBAAkB,EAAE,CAAC;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3B,OAAO,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAA6B,EAC7B,GAAmB,EACnB,QAAkB,EAClB,OAA0B;IAE1B,OAAO,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/i18n/vite/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/i18n/vite/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A site's catalogs as the build reads them: all of them, off disk, at once.
|
|
3
|
+
*
|
|
4
|
+
* The page and the build want opposite things from the same files. A browser
|
|
5
|
+
* fetches one language — the one being read — so `en.json` is part of the
|
|
6
|
+
* bundle and the others are `import()`ed when a visitor asks, which is what
|
|
7
|
+
* keeps a site nobody reads in German from downloading the German. The build
|
|
8
|
+
* writes a head per address per language in one pass, so it wants every
|
|
9
|
+
* language, synchronously, before the first route table is asked for.
|
|
10
|
+
*
|
|
11
|
+
* Reading them rather than fetching them is what makes that possible. The
|
|
12
|
+
* page's loaders are written bare — `import('../locales/fr.json')` with no
|
|
13
|
+
* `with { type: 'json' }` — because the dev server serves a JSON file as a
|
|
14
|
+
* JavaScript module and a browser refuses a chunk it was promised JSON for.
|
|
15
|
+
* Node demands the attribute, so it cannot run those loaders: a build that
|
|
16
|
+
* awaited them had to be async and still only worked by keeping a second,
|
|
17
|
+
* eagerly imported copy of every catalog beside the first. `readFileSync` has
|
|
18
|
+
* no attribute, so there is nothing to work around.
|
|
19
|
+
*
|
|
20
|
+
* The identity comes from the page's own catalog, never re-typed here: pass
|
|
21
|
+
* `SITE_CATALOG.source` and the two provably name the same files.
|
|
22
|
+
*/
|
|
23
|
+
import { MessageCatalog } from '../core/index.ts';
|
|
24
|
+
/**
|
|
25
|
+
* Which catalog to read, which is the page catalog's own `source`.
|
|
26
|
+
*
|
|
27
|
+
* `MessageCatalog` publishes it for the translate overlay, and it already
|
|
28
|
+
* carries the three things a reader needs. Hand it over rather than writing
|
|
29
|
+
* them twice: a build that names its own directory is a build that can come to
|
|
30
|
+
* disagree with the page about which files the site is translated in.
|
|
31
|
+
*/
|
|
32
|
+
export interface LocaleCatalogSource {
|
|
33
|
+
/**
|
|
34
|
+
* The name the page knows the catalog by, which is the site's host, e.g.
|
|
35
|
+
* `surge.cheminfo.org`. A translator's edit is routed back to the right
|
|
36
|
+
* repository with it, so the build and the page must agree on it.
|
|
37
|
+
*/
|
|
38
|
+
id: string;
|
|
39
|
+
/** The GitHub repository the files live in, written `owner/repo`. */
|
|
40
|
+
repository: string;
|
|
41
|
+
/** The directory holding the locale files, relative to the repository root. */
|
|
42
|
+
directory: string;
|
|
43
|
+
}
|
|
44
|
+
/** Where the directory is looked for. */
|
|
45
|
+
export interface LocaleCatalogOptions {
|
|
46
|
+
/**
|
|
47
|
+
* What the source's relative `directory` is resolved against. The default is
|
|
48
|
+
* the working directory, which is the repository root for both callers: a
|
|
49
|
+
* Vite config and a vitest run.
|
|
50
|
+
* @default process.cwd()
|
|
51
|
+
*/
|
|
52
|
+
root?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Every language a site is written in, read off disk into one catalog.
|
|
56
|
+
*
|
|
57
|
+
* For the build and for a site's own tests, never for the page: it reads the
|
|
58
|
+
* filesystem, and the translations it holds are tens of kilobytes a first
|
|
59
|
+
* visit must not pay for. Read one language out of it with `catalogTranslate`.
|
|
60
|
+
*
|
|
61
|
+
* A language whose file is absent is left out, which the catalog already reads
|
|
62
|
+
* as English — a half-translated site serves the pages it has rather than
|
|
63
|
+
* failing the build. A missing English file is a misconfiguration and throws:
|
|
64
|
+
* every key and every fallback is declared there.
|
|
65
|
+
* @param source - The page catalog's own `source`. See {@link LocaleCatalogSource}.
|
|
66
|
+
* @param options - See {@link LocaleCatalogOptions}.
|
|
67
|
+
* @returns The catalog, every language it carries already in memory.
|
|
68
|
+
*/
|
|
69
|
+
export declare function readLocaleCatalog<TKey extends string = string>(source: LocaleCatalogSource, options?: LocaleCatalogOptions): MessageCatalog<TKey>;
|
|
70
|
+
//# sourceMappingURL=localeCatalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localeCatalog.d.ts","sourceRoot":"","sources":["../../../src/i18n/vite/localeCatalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,OAAO,EAA+B,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE/E;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAC5D,MAAM,EAAE,mBAAmB,EAC3B,OAAO,GAAE,oBAAyB,GACjC,cAAc,CAAC,IAAI,CAAC,CAsBtB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A site's catalogs as the build reads them: all of them, off disk, at once.
|
|
3
|
+
*
|
|
4
|
+
* The page and the build want opposite things from the same files. A browser
|
|
5
|
+
* fetches one language — the one being read — so `en.json` is part of the
|
|
6
|
+
* bundle and the others are `import()`ed when a visitor asks, which is what
|
|
7
|
+
* keeps a site nobody reads in German from downloading the German. The build
|
|
8
|
+
* writes a head per address per language in one pass, so it wants every
|
|
9
|
+
* language, synchronously, before the first route table is asked for.
|
|
10
|
+
*
|
|
11
|
+
* Reading them rather than fetching them is what makes that possible. The
|
|
12
|
+
* page's loaders are written bare — `import('../locales/fr.json')` with no
|
|
13
|
+
* `with { type: 'json' }` — because the dev server serves a JSON file as a
|
|
14
|
+
* JavaScript module and a browser refuses a chunk it was promised JSON for.
|
|
15
|
+
* Node demands the attribute, so it cannot run those loaders: a build that
|
|
16
|
+
* awaited them had to be async and still only worked by keeping a second,
|
|
17
|
+
* eagerly imported copy of every catalog beside the first. `readFileSync` has
|
|
18
|
+
* no attribute, so there is nothing to work around.
|
|
19
|
+
*
|
|
20
|
+
* The identity comes from the page's own catalog, never re-typed here: pass
|
|
21
|
+
* `SITE_CATALOG.source` and the two provably name the same files.
|
|
22
|
+
*/
|
|
23
|
+
import { readFileSync } from 'node:fs';
|
|
24
|
+
import { join, resolve } from 'node:path';
|
|
25
|
+
import { DEFAULT_LANGUAGE, LANGUAGES, MessageCatalog } from "../core/index.js";
|
|
26
|
+
/**
|
|
27
|
+
* Every language a site is written in, read off disk into one catalog.
|
|
28
|
+
*
|
|
29
|
+
* For the build and for a site's own tests, never for the page: it reads the
|
|
30
|
+
* filesystem, and the translations it holds are tens of kilobytes a first
|
|
31
|
+
* visit must not pay for. Read one language out of it with `catalogTranslate`.
|
|
32
|
+
*
|
|
33
|
+
* A language whose file is absent is left out, which the catalog already reads
|
|
34
|
+
* as English — a half-translated site serves the pages it has rather than
|
|
35
|
+
* failing the build. A missing English file is a misconfiguration and throws:
|
|
36
|
+
* every key and every fallback is declared there.
|
|
37
|
+
* @param source - The page catalog's own `source`. See {@link LocaleCatalogSource}.
|
|
38
|
+
* @param options - See {@link LocaleCatalogOptions}.
|
|
39
|
+
* @returns The catalog, every language it carries already in memory.
|
|
40
|
+
*/
|
|
41
|
+
export function readLocaleCatalog(source, options = {}) {
|
|
42
|
+
const { id, repository, directory } = source;
|
|
43
|
+
const folder = resolve(options.root ?? process.cwd(), directory);
|
|
44
|
+
const english = readMessages(folder, DEFAULT_LANGUAGE);
|
|
45
|
+
if (english === undefined) {
|
|
46
|
+
throw new Error(`no ${DEFAULT_LANGUAGE}.json in ${folder}: a catalog declares its keys and its fallbacks there`);
|
|
47
|
+
}
|
|
48
|
+
const translations = {};
|
|
49
|
+
for (const language of LANGUAGES) {
|
|
50
|
+
if (language === DEFAULT_LANGUAGE)
|
|
51
|
+
continue;
|
|
52
|
+
const messages = readMessages(folder, language);
|
|
53
|
+
if (messages !== undefined)
|
|
54
|
+
translations[language] = messages;
|
|
55
|
+
}
|
|
56
|
+
return new MessageCatalog({
|
|
57
|
+
id,
|
|
58
|
+
repository,
|
|
59
|
+
directory,
|
|
60
|
+
source: english,
|
|
61
|
+
translations,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One language's messages, read off disk.
|
|
66
|
+
* @param folder - The resolved directory holding the locale files.
|
|
67
|
+
* @param language - The language whose file to read.
|
|
68
|
+
* @returns Its messages, or `undefined` when the site does not carry it.
|
|
69
|
+
*/
|
|
70
|
+
function readMessages(folder, language) {
|
|
71
|
+
let text;
|
|
72
|
+
try {
|
|
73
|
+
text = readFileSync(join(folder, `${language}.json`), 'utf8');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
return JSON.parse(text);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=localeCatalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localeCatalog.js","sourceRoot":"","sources":["../../../src/i18n/vite/localeCatalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAkC/E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA2B,EAC3B,UAAgC,EAAE;IAElC,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACvD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,MAAM,gBAAgB,YAAY,MAAM,uDAAuD,CAChG,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAsD,EAAE,CAAC;IAC3E,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,QAAQ,KAAK,gBAAgB;YAAE,SAAS;QAC5C,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChD,IAAI,QAAQ,KAAK,SAAS;YAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,cAAc,CAAO;QAC9B,EAAE;QACF,UAAU;QACV,SAAS;QACT,MAAM,EAAE,OAAO;QACf,YAAY;KACb,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CACnB,MAAc,EACd,QAAkB;IAElB,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,QAAQ,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAA2B,CAAC;AACpD,CAAC"}
|
package/lib/locales/de.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"chrome.pages": "Seiten",
|
|
4
4
|
"chrome.about": "Über",
|
|
5
5
|
"chrome.aboutTitle": "Was diese Seite ist, worauf sie aufbaut und wie man sie zitiert",
|
|
6
|
+
"chrome.contact": "Kontakt",
|
|
7
|
+
"chrome.contactTitle": "Eine Frage, ein Problem, eine Idee? Schreiben Sie uns",
|
|
6
8
|
"account.signIn": "Anmelden",
|
|
7
9
|
"account.signOut": "Abmelden",
|
|
8
10
|
"account.signedInAs": "Angemeldet als {name}",
|
package/lib/locales/en.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"chrome.pages": "Pages",
|
|
4
4
|
"chrome.about": "About",
|
|
5
5
|
"chrome.aboutTitle": "What this site is, what it is built on, and how to cite it",
|
|
6
|
+
"chrome.contact": "Contact",
|
|
7
|
+
"chrome.contactTitle": "A question, a problem or an idea? Write to us",
|
|
6
8
|
"account.signIn": "Sign in",
|
|
7
9
|
"account.signOut": "Sign out",
|
|
8
10
|
"account.signedInAs": "Signed in as {name}",
|
package/lib/locales/es.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"chrome.pages": "Páginas",
|
|
4
4
|
"chrome.about": "Acerca de",
|
|
5
5
|
"chrome.aboutTitle": "Qué es este sitio, en qué se basa y cómo citarlo",
|
|
6
|
+
"chrome.contact": "Contacto",
|
|
7
|
+
"chrome.contactTitle": "¿Una pregunta, un problema, una idea? Escríbanos",
|
|
6
8
|
"account.signIn": "Iniciar sesión",
|
|
7
9
|
"account.signOut": "Cerrar sesión",
|
|
8
10
|
"account.signedInAs": "Sesión iniciada como {name}",
|
package/lib/locales/fr.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"chrome.pages": "Pages",
|
|
4
4
|
"chrome.about": "À propos",
|
|
5
5
|
"chrome.aboutTitle": "Ce qu'est ce site, sur quoi il est bâti et comment le citer",
|
|
6
|
+
"chrome.contact": "Contact",
|
|
7
|
+
"chrome.contactTitle": "Une question, un problème, une idée ? Écrivez-nous",
|
|
6
8
|
"account.signIn": "Se connecter",
|
|
7
9
|
"account.signOut": "Se déconnecter",
|
|
8
10
|
"account.signedInAs": "Connecté comme {name}",
|
package/lib/locales/it.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"chrome.pages": "Pagine",
|
|
4
4
|
"chrome.about": "Informazioni",
|
|
5
5
|
"chrome.aboutTitle": "Cosa è questo sito, su cosa si basa e come citarlo",
|
|
6
|
+
"chrome.contact": "Contatto",
|
|
7
|
+
"chrome.contactTitle": "Una domanda, un problema, un'idea? Scriveteci",
|
|
6
8
|
"account.signIn": "Accedi",
|
|
7
9
|
"account.signOut": "Esci",
|
|
8
10
|
"account.signedInAs": "Accesso effettuato come {name}",
|
package/lib/ui.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from './capsule/ui/index.ts';
|
|
|
4
4
|
export * from './chart/ui/index.ts';
|
|
5
5
|
export * from './chrome/ui/index.ts';
|
|
6
6
|
export * from './citation/ui/index.ts';
|
|
7
|
+
export * from './contact/ui/index.ts';
|
|
7
8
|
export * from './clipboard/ui/index.ts';
|
|
8
9
|
export * from './color/ui/index.ts';
|
|
9
10
|
export * from './confirm/ui/index.ts';
|
package/lib/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAKvC,YAAY,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,eAAe,GAChB,MAAM,oCAAoC,CAAC"}
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAKvC,YAAY,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,eAAe,GAChB,MAAM,oCAAoC,CAAC"}
|
package/lib/ui.js
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./capsule/ui/index.js";
|
|
|
4
4
|
export * from "./chart/ui/index.js";
|
|
5
5
|
export * from "./chrome/ui/index.js";
|
|
6
6
|
export * from "./citation/ui/index.js";
|
|
7
|
+
export * from "./contact/ui/index.js";
|
|
7
8
|
export * from "./clipboard/ui/index.js";
|
|
8
9
|
export * from "./color/ui/index.js";
|
|
9
10
|
export * from "./confirm/ui/index.js";
|
package/lib/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAMvC,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAmC,CAAC;AAEnE,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,eAAe,GAChB,MAAM,oCAAoC,CAAC"}
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AAMvC,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAmC,CAAC;AAEnE,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,eAAe,GAChB,MAAM,oCAAoC,CAAC"}
|
package/lib/vite.d.ts
CHANGED
package/lib/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC"}
|
package/lib/vite.js
CHANGED
package/lib/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-cheminfo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.55.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React components, hooks and framework-free helpers shared by the cheminfo sites: site chrome, citations, sharing, routing, pedagogy, charts, and the structure, orbital and 3D molecule viewers",
|
|
6
6
|
"author": "Luc Patiny",
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { ReactElement, ReactNode } from 'react';
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
3
|
|
|
4
|
+
import { contactFormUrl } from '../../contact/core/contactUrl.ts';
|
|
5
|
+
import { ContactButton } from '../../contact/ui/ContactButton.tsx';
|
|
4
6
|
import { siteById } from '../../ecosystem/core/lookup.ts';
|
|
5
7
|
import type { SiteId, SiteRecord } from '../../ecosystem/core/sites.ts';
|
|
6
8
|
import { Wordmark } from '../../ecosystem/ui/Wordmark.tsx';
|
|
7
9
|
import { SiteMark } from '../../ecosystem/ui/marks.tsx';
|
|
10
|
+
import { useLanguage } from '../../i18n/ui/useLanguage.ts';
|
|
8
11
|
import { useChromeT } from '../../i18n/ui/useT.ts';
|
|
9
12
|
|
|
10
13
|
import { NavLink } from './NavLink.tsx';
|
|
@@ -39,12 +42,28 @@ export interface SiteHeaderProps {
|
|
|
39
42
|
*/
|
|
40
43
|
activeId?: string;
|
|
41
44
|
/**
|
|
42
|
-
* The utilities pushed to the right edge — Cite, Tools,
|
|
45
|
+
* The utilities pushed to the right edge — About, Cite, Tools, the language,
|
|
46
|
+
* sign in; Share has a prop of its own, so it always comes last. They
|
|
43
47
|
* arrive dressed as bar items, so a plain `nav-link` and a `CiteButton` read
|
|
44
48
|
* alike beside each other.
|
|
45
49
|
* @default undefined
|
|
46
50
|
*/
|
|
47
51
|
actions?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* The Share entry, which closes the utilities on every site, after the
|
|
54
|
+
* Contact entry. A site passes its own when it wraps the shared `ShareButton`
|
|
55
|
+
* in a dialog of its own.
|
|
56
|
+
* @default undefined
|
|
57
|
+
*/
|
|
58
|
+
share?: ReactNode;
|
|
59
|
+
/**
|
|
60
|
+
* The contact form the Contact entry opens, drawn after the site's utilities
|
|
61
|
+
* and just before Share, with
|
|
62
|
+
* `{site}` and `{page}` where the site and the page are written; `false`
|
|
63
|
+
* leaves the entry out. No entry is drawn while there is no form.
|
|
64
|
+
* @default the family's form in the language of the page (`CONTACT_FORM_URLS`)
|
|
65
|
+
*/
|
|
66
|
+
contact?: string | false;
|
|
48
67
|
/**
|
|
49
68
|
* Draws one page the site's own way, for a bar whose entries need a tooltip
|
|
50
69
|
* or a wrapper of their own.
|
|
@@ -113,6 +132,8 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
113
132
|
nav,
|
|
114
133
|
activeId,
|
|
115
134
|
actions,
|
|
135
|
+
share,
|
|
136
|
+
contact,
|
|
116
137
|
renderNavItem,
|
|
117
138
|
embedded = false,
|
|
118
139
|
homeHref = '/',
|
|
@@ -122,6 +143,7 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
122
143
|
pagesLabel,
|
|
123
144
|
} = props;
|
|
124
145
|
const t = useChromeT();
|
|
146
|
+
const language = useLanguage();
|
|
125
147
|
|
|
126
148
|
if (embedded) return null;
|
|
127
149
|
|
|
@@ -129,6 +151,8 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
129
151
|
if (site === undefined) {
|
|
130
152
|
throw new Error('SiteHeader needs one of its `site` and `siteId` props');
|
|
131
153
|
}
|
|
154
|
+
const contactForm =
|
|
155
|
+
contact === false ? '' : (contact ?? contactFormUrl(language));
|
|
132
156
|
|
|
133
157
|
return (
|
|
134
158
|
<header className="app-header no-print">
|
|
@@ -169,8 +193,16 @@ export function SiteHeader(props: SiteHeaderProps): ReactElement | null {
|
|
|
169
193
|
</div>
|
|
170
194
|
) : null}
|
|
171
195
|
<span className="spacer" />
|
|
172
|
-
{actions === undefined
|
|
173
|
-
|
|
196
|
+
{actions === undefined &&
|
|
197
|
+
share === undefined &&
|
|
198
|
+
contactForm === '' ? null : (
|
|
199
|
+
<div className="app-header-actions">
|
|
200
|
+
{actions}
|
|
201
|
+
{contactForm === '' ? null : (
|
|
202
|
+
<ContactButton siteId={site.id} formUrl={contactForm} />
|
|
203
|
+
)}
|
|
204
|
+
{share}
|
|
205
|
+
</div>
|
|
174
206
|
)}
|
|
175
207
|
</div>
|
|
176
208
|
</header>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Language } from '../../i18n/core/languages.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Address of the contact form every site of the family opens, one per language the form
|
|
5
|
+
* is written in, with `{site}` and `{page}` where the site and the page are written. A
|
|
6
|
+
* language without a form of its own opens the English one; with no form at all, no
|
|
7
|
+
* Contact entry is drawn.
|
|
8
|
+
*/
|
|
9
|
+
export const CONTACT_FORM_URLS: Readonly<Partial<Record<Language, string>>> = {
|
|
10
|
+
en: 'https://docs.google.com/forms/d/e/1FAIpQLSd_1G3tUOEJh_qapmLn_xrybWa9Nu7B57bpYm76o4nwPKPWsg/viewform?usp=pp_url&entry.569380734={site}&entry.2103045774={page}',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** What a contact address is filled with. */
|
|
14
|
+
export interface ContactUrlOptions {
|
|
15
|
+
/** The site the message is about, written as its id. */
|
|
16
|
+
site: string;
|
|
17
|
+
/**
|
|
18
|
+
* The address of the page the reader was on.
|
|
19
|
+
* @default ''
|
|
20
|
+
*/
|
|
21
|
+
page?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The contact form in the language of the page, or the English one when the form is not
|
|
26
|
+
* written in that language.
|
|
27
|
+
* @param language - The language of the page.
|
|
28
|
+
* @param urls - The forms, by language.
|
|
29
|
+
* @returns The address of the form, or an empty string when there is none.
|
|
30
|
+
*/
|
|
31
|
+
export function contactFormUrl(
|
|
32
|
+
language: Language,
|
|
33
|
+
urls: Readonly<Partial<Record<Language, string>>> = CONTACT_FORM_URLS,
|
|
34
|
+
): string {
|
|
35
|
+
return urls[language] ?? urls.en ?? '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Fill the address of a contact form with the site and the page, both encoded, so the
|
|
40
|
+
* form opens knowing what the message is about.
|
|
41
|
+
* @param template - Address holding `{site}` and `{page}`.
|
|
42
|
+
* @param options - The site and the page.
|
|
43
|
+
* @returns The address to open.
|
|
44
|
+
*/
|
|
45
|
+
export function contactUrl(
|
|
46
|
+
template: string,
|
|
47
|
+
options: ContactUrlOptions,
|
|
48
|
+
): string {
|
|
49
|
+
const { site, page = '' } = options;
|
|
50
|
+
return template
|
|
51
|
+
.replaceAll('{site}', encodeURIComponent(site))
|
|
52
|
+
.replaceAll('{page}', encodeURIComponent(page));
|
|
53
|
+
}
|