@myst-theme/site 0.9.7 → 0.9.9
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/package.json +8 -8
- package/src/components/Abstract.tsx +3 -49
- package/src/components/Backmatter.tsx +24 -22
- package/src/components/Bibliography.tsx +18 -4
- package/src/components/ContentBlocks.tsx +2 -6
- package/src/components/DocumentOutline.tsx +2 -2
- package/src/components/FooterLinksBlock.tsx +1 -1
- package/src/components/Footnotes.tsx +18 -4
- package/src/components/FrontmatterParts.tsx +37 -0
- package/src/components/Keywords.tsx +30 -0
- package/src/components/Navigation/ActionMenu.tsx +48 -0
- package/src/components/Navigation/HomeLink.tsx +55 -0
- package/src/components/Navigation/Loading.tsx +1 -1
- package/src/components/Navigation/TopNav.tsx +5 -105
- package/src/components/Navigation/index.tsx +3 -1
- package/src/components/SkipToArticle.tsx +28 -1
- package/src/components/index.ts +12 -5
- package/src/pages/Article.tsx +1 -19
- package/src/seo/meta.ts +2 -2
- package/src/utils.ts +40 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myst-theme/site",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -17,19 +17,19 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@headlessui/react": "^1.7.15",
|
|
19
19
|
"@heroicons/react": "^2.0.18",
|
|
20
|
-
"@myst-theme/common": "^0.9.
|
|
21
|
-
"@myst-theme/diagrams": "^0.9.
|
|
22
|
-
"@myst-theme/frontmatter": "^0.9.
|
|
23
|
-
"@myst-theme/jupyter": "^0.9.
|
|
24
|
-
"@myst-theme/providers": "^0.9.
|
|
20
|
+
"@myst-theme/common": "^0.9.9",
|
|
21
|
+
"@myst-theme/diagrams": "^0.9.9",
|
|
22
|
+
"@myst-theme/frontmatter": "^0.9.9",
|
|
23
|
+
"@myst-theme/jupyter": "^0.9.9",
|
|
24
|
+
"@myst-theme/providers": "^0.9.9",
|
|
25
25
|
"@radix-ui/react-collapsible": "^1.0.3",
|
|
26
26
|
"classnames": "^2.3.2",
|
|
27
27
|
"lodash.throttle": "^4.1.1",
|
|
28
28
|
"myst-common": "^1.4.4",
|
|
29
29
|
"myst-config": "^1.4.4",
|
|
30
|
-
"myst-demo": "^0.9.
|
|
30
|
+
"myst-demo": "^0.9.9",
|
|
31
31
|
"myst-spec-ext": "^1.4.4",
|
|
32
|
-
"myst-to-react": "^0.9.
|
|
32
|
+
"myst-to-react": "^0.9.9",
|
|
33
33
|
"nbtx": "^0.2.3",
|
|
34
34
|
"node-cache": "^5.1.2",
|
|
35
35
|
"node-fetch": "^2.6.11",
|
|
@@ -1,41 +1,21 @@
|
|
|
1
1
|
import type { GenericParent } from 'myst-common';
|
|
2
2
|
import { ContentBlocks } from './ContentBlocks.js';
|
|
3
|
-
import classNames from 'classnames';
|
|
4
3
|
import { HashLink } from 'myst-to-react';
|
|
5
|
-
import { type KnownParts } from '../utils.js';
|
|
6
|
-
|
|
7
|
-
export function FrontmatterParts({
|
|
8
|
-
parts,
|
|
9
|
-
keywords,
|
|
10
|
-
hideKeywords,
|
|
11
|
-
}: {
|
|
12
|
-
parts: KnownParts;
|
|
13
|
-
keywords?: string[];
|
|
14
|
-
hideKeywords?: boolean;
|
|
15
|
-
}) {
|
|
16
|
-
if (!parts.abstract && !parts.keypoints && !parts.summary) return null;
|
|
17
|
-
return (
|
|
18
|
-
<>
|
|
19
|
-
<Abstract content={parts.abstract} />
|
|
20
|
-
<Abstract content={parts.keypoints} title="Key Points" id="keypoints" />
|
|
21
|
-
<Abstract content={parts.summary} title="Plain Language Summary" id="summary" />
|
|
22
|
-
<Keywords keywords={keywords} hideKeywords={hideKeywords} />
|
|
23
|
-
</>
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
4
|
|
|
27
5
|
export function Abstract({
|
|
28
6
|
content,
|
|
29
7
|
title = 'Abstract',
|
|
30
8
|
id = 'abstract',
|
|
9
|
+
className,
|
|
31
10
|
}: {
|
|
32
11
|
title?: string;
|
|
33
12
|
id?: string;
|
|
34
13
|
content?: GenericParent;
|
|
14
|
+
className?: string;
|
|
35
15
|
}) {
|
|
36
16
|
if (!content) return null;
|
|
37
17
|
return (
|
|
38
|
-
|
|
18
|
+
<div className={className}>
|
|
39
19
|
<h2 id={id} className="mb-3 text-base font-semibold group">
|
|
40
20
|
{title}
|
|
41
21
|
<HashLink id={id} title={`Link to ${title}`} hover className="ml-2" />
|
|
@@ -43,32 +23,6 @@ export function Abstract({
|
|
|
43
23
|
<div className="px-6 py-1 mb-3 rounded-sm bg-slate-50 dark:bg-slate-800">
|
|
44
24
|
<ContentBlocks mdast={content} className="col-body" />
|
|
45
25
|
</div>
|
|
46
|
-
</>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function Keywords({
|
|
51
|
-
keywords,
|
|
52
|
-
hideKeywords,
|
|
53
|
-
}: {
|
|
54
|
-
keywords?: string[];
|
|
55
|
-
hideKeywords?: boolean;
|
|
56
|
-
}) {
|
|
57
|
-
if (hideKeywords || !keywords || keywords.length === 0) return null;
|
|
58
|
-
return (
|
|
59
|
-
<div className="mb-10 group">
|
|
60
|
-
<span className="mr-2 font-semibold">Keywords:</span>
|
|
61
|
-
{keywords.map((k, i) => (
|
|
62
|
-
<span
|
|
63
|
-
key={k}
|
|
64
|
-
className={classNames({
|
|
65
|
-
"after:content-[','] after:mr-1": i < keywords.length - 1,
|
|
66
|
-
})}
|
|
67
|
-
>
|
|
68
|
-
{k}
|
|
69
|
-
</span>
|
|
70
|
-
))}
|
|
71
|
-
<HashLink id="keywords" title="Link to Keywords" hover className="ml-2" />
|
|
72
26
|
</div>
|
|
73
27
|
);
|
|
74
28
|
}
|
|
@@ -1,47 +1,49 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GenericParent } from 'myst-common';
|
|
2
2
|
import { HashLink, MyST } from 'myst-to-react';
|
|
3
|
-
import type
|
|
3
|
+
import { getChildren, type KnownParts } from '../utils.js';
|
|
4
|
+
import classNames from 'classnames';
|
|
4
5
|
|
|
5
|
-
export function BackmatterParts({
|
|
6
|
+
export function BackmatterParts({
|
|
7
|
+
parts,
|
|
8
|
+
containerClassName,
|
|
9
|
+
innerClassName,
|
|
10
|
+
}: {
|
|
11
|
+
parts: KnownParts;
|
|
12
|
+
containerClassName?: string;
|
|
13
|
+
innerClassName?: string;
|
|
14
|
+
}) {
|
|
6
15
|
return (
|
|
7
|
-
|
|
8
|
-
<Backmatter
|
|
16
|
+
<div className={containerClassName}>
|
|
17
|
+
<Backmatter
|
|
18
|
+
className={innerClassName}
|
|
19
|
+
title="Acknowledgments"
|
|
20
|
+
id="acknowledgments"
|
|
21
|
+
content={parts.acknowledgments}
|
|
22
|
+
/>
|
|
9
23
|
<Backmatter
|
|
24
|
+
className={innerClassName}
|
|
10
25
|
title="Data Availability"
|
|
11
26
|
id="data-availability"
|
|
12
27
|
content={parts.data_availability}
|
|
13
28
|
/>
|
|
14
|
-
|
|
29
|
+
</div>
|
|
15
30
|
);
|
|
16
31
|
}
|
|
17
32
|
|
|
18
|
-
/**
|
|
19
|
-
* This returns the contents of a part that we want to render (not the root or block, which are already wrapped)
|
|
20
|
-
* This also fixes a bug that the key is not defined on a block.
|
|
21
|
-
*/
|
|
22
|
-
function getChildren(content?: GenericParent): GenericNode | GenericNode[] {
|
|
23
|
-
if (
|
|
24
|
-
content?.type === 'root' &&
|
|
25
|
-
content.children?.length === 1 &&
|
|
26
|
-
content.children[0].type === 'block'
|
|
27
|
-
) {
|
|
28
|
-
return content.children[0].children as GenericNode[];
|
|
29
|
-
}
|
|
30
|
-
return content as GenericNode;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
33
|
export function Backmatter({
|
|
34
34
|
title,
|
|
35
35
|
id,
|
|
36
36
|
content,
|
|
37
|
+
className,
|
|
37
38
|
}: {
|
|
38
39
|
title: string;
|
|
39
40
|
id: string;
|
|
40
41
|
content?: GenericParent;
|
|
42
|
+
className?: string;
|
|
41
43
|
}) {
|
|
42
44
|
if (!content) return null;
|
|
43
45
|
return (
|
|
44
|
-
<div className=
|
|
46
|
+
<div className={classNames('flex flex-col w-full md:flex-row group/backmatter', className)}>
|
|
45
47
|
<h2
|
|
46
48
|
id={id}
|
|
47
49
|
className="mt-5 text-base font-semibold group md:w-[200px] self-start md:flex-none opacity-90 group-hover/backmatter:opacity-100"
|
|
@@ -5,7 +5,13 @@ import { useState } from 'react';
|
|
|
5
5
|
|
|
6
6
|
const HIDE_OVER_N_REFERENCES = 5;
|
|
7
7
|
|
|
8
|
-
export function Bibliography(
|
|
8
|
+
export function Bibliography({
|
|
9
|
+
containerClassName,
|
|
10
|
+
innerClassName,
|
|
11
|
+
}: {
|
|
12
|
+
containerClassName?: string;
|
|
13
|
+
innerClassName?: string;
|
|
14
|
+
}) {
|
|
9
15
|
const references = useReferences();
|
|
10
16
|
const grid = useGridSystemProvider();
|
|
11
17
|
const { order, data } = references?.cite ?? {};
|
|
@@ -14,8 +20,11 @@ export function Bibliography() {
|
|
|
14
20
|
if (!filtered || !data || filtered.length === 0) return null;
|
|
15
21
|
const refs = hidden ? filtered.slice(0, HIDE_OVER_N_REFERENCES) : filtered;
|
|
16
22
|
return (
|
|
17
|
-
<section
|
|
18
|
-
|
|
23
|
+
<section
|
|
24
|
+
id="references"
|
|
25
|
+
className={classNames(grid, 'subgrid-gap col-screen', containerClassName)}
|
|
26
|
+
>
|
|
27
|
+
<div className={innerClassName}>
|
|
19
28
|
{filtered.length > HIDE_OVER_N_REFERENCES && (
|
|
20
29
|
<button
|
|
21
30
|
onClick={() => setHidden(!hidden)}
|
|
@@ -29,7 +38,12 @@ export function Bibliography() {
|
|
|
29
38
|
<HashLink id="references" title="Link to References" hover className="ml-2" />
|
|
30
39
|
</header>
|
|
31
40
|
</div>
|
|
32
|
-
<div
|
|
41
|
+
<div
|
|
42
|
+
className={classNames(
|
|
43
|
+
'pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300',
|
|
44
|
+
innerClassName,
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
33
47
|
<ol>
|
|
34
48
|
{refs.map((label) => {
|
|
35
49
|
const { html } = data[label];
|
|
@@ -3,18 +3,14 @@ import { SourceFileKind } from 'myst-spec-ext';
|
|
|
3
3
|
import type { GenericParent } from 'myst-common';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
5
|
import {
|
|
6
|
-
executableNodesFromBlock,
|
|
7
6
|
NotebookClearCell,
|
|
8
7
|
NotebookRunCell,
|
|
9
8
|
NotebookRunCellSpinnerOnly,
|
|
10
9
|
} from '@myst-theme/jupyter';
|
|
11
10
|
import { useGridSystemProvider } from '@myst-theme/providers';
|
|
11
|
+
import { isACodeCell } from '../utils.js';
|
|
12
12
|
|
|
13
|
-
function
|
|
14
|
-
return !!executableNodesFromBlock(node);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function Block({
|
|
13
|
+
export function Block({
|
|
18
14
|
id,
|
|
19
15
|
pageKind,
|
|
20
16
|
node,
|
|
@@ -15,7 +15,7 @@ const HIGHLIGHT_CLASS = 'highlight';
|
|
|
15
15
|
|
|
16
16
|
const onClient = typeof document !== 'undefined';
|
|
17
17
|
|
|
18
|
-
type Heading = {
|
|
18
|
+
export type Heading = {
|
|
19
19
|
element: HTMLHeadingElement;
|
|
20
20
|
id: string;
|
|
21
21
|
title: string;
|
|
@@ -105,7 +105,7 @@ function getHeaders(selector: string): HTMLHeadingElement[] {
|
|
|
105
105
|
return headers as HTMLHeadingElement[];
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function useHeaders(selector: string, maxdepth: number) {
|
|
108
|
+
export function useHeaders(selector: string, maxdepth: number) {
|
|
109
109
|
if (!onClient) return { activeId: '', headings: [] };
|
|
110
110
|
const onScreen = useRef<Set<HTMLHeadingElement>>(new Set());
|
|
111
111
|
const [activeId, setActiveId] = useState<string>();
|
|
@@ -3,7 +3,7 @@ import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline';
|
|
|
3
3
|
import type { FooterLinks, NavigationLink } from '@myst-theme/common';
|
|
4
4
|
import { useLinkProvider, useBaseurl, withBaseurl } from '@myst-theme/providers';
|
|
5
5
|
|
|
6
|
-
const FooterLink = ({
|
|
6
|
+
export const FooterLink = ({
|
|
7
7
|
title,
|
|
8
8
|
short_title,
|
|
9
9
|
url,
|
|
@@ -5,21 +5,35 @@ import type { FootnoteDefinition, FootnoteReference } from 'myst-spec-ext';
|
|
|
5
5
|
import { HashLink, MyST } from 'myst-to-react';
|
|
6
6
|
import { selectAll } from 'unist-util-select';
|
|
7
7
|
|
|
8
|
-
export function Footnotes(
|
|
8
|
+
export function Footnotes({
|
|
9
|
+
containerClassName,
|
|
10
|
+
innerClassName,
|
|
11
|
+
}: {
|
|
12
|
+
containerClassName?: string;
|
|
13
|
+
innerClassName?: string;
|
|
14
|
+
}) {
|
|
9
15
|
const references = useReferences();
|
|
10
16
|
const grid = useGridSystemProvider();
|
|
11
17
|
const defs = selectAll('footnoteDefinition', references?.article) as FootnoteDefinition[];
|
|
12
18
|
const refs = selectAll('footnoteReference', references?.article) as FootnoteReference[];
|
|
13
19
|
if (defs.length === 0) return null;
|
|
14
20
|
return (
|
|
15
|
-
<section
|
|
16
|
-
|
|
21
|
+
<section
|
|
22
|
+
id="footnotes"
|
|
23
|
+
className={classNames(grid, 'subgrid-gap col-screen', containerClassName)}
|
|
24
|
+
>
|
|
25
|
+
<div className={innerClassName}>
|
|
17
26
|
<header className="text-lg font-semibold text-stone-900 dark:text-white group">
|
|
18
27
|
Footnotes
|
|
19
28
|
<HashLink id="footnotes" title="Link to Footnotes" hover className="ml-2" />
|
|
20
29
|
</header>
|
|
21
30
|
</div>
|
|
22
|
-
<div
|
|
31
|
+
<div
|
|
32
|
+
className={classNames(
|
|
33
|
+
'pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300',
|
|
34
|
+
innerClassName,
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
23
37
|
<ol>
|
|
24
38
|
{defs.map((fn) => {
|
|
25
39
|
return (
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type KnownParts } from '../utils.js';
|
|
2
|
+
import { Abstract } from './Abstract.js';
|
|
3
|
+
import { Keywords } from './Keywords.js';
|
|
4
|
+
|
|
5
|
+
export function FrontmatterParts({
|
|
6
|
+
parts,
|
|
7
|
+
keywords,
|
|
8
|
+
hideKeywords,
|
|
9
|
+
containerClassName,
|
|
10
|
+
innerClassName,
|
|
11
|
+
}: {
|
|
12
|
+
parts: KnownParts;
|
|
13
|
+
keywords?: string[];
|
|
14
|
+
hideKeywords?: boolean;
|
|
15
|
+
containerClassName?: string;
|
|
16
|
+
innerClassName?: string;
|
|
17
|
+
}) {
|
|
18
|
+
if (!parts.abstract && !parts.keypoints && !parts.summary) return null;
|
|
19
|
+
return (
|
|
20
|
+
<div className={containerClassName}>
|
|
21
|
+
<Abstract className={innerClassName} content={parts.abstract} />
|
|
22
|
+
<Abstract
|
|
23
|
+
className={innerClassName}
|
|
24
|
+
content={parts.keypoints}
|
|
25
|
+
title="Key Points"
|
|
26
|
+
id="keypoints"
|
|
27
|
+
/>
|
|
28
|
+
<Abstract
|
|
29
|
+
className={innerClassName}
|
|
30
|
+
content={parts.summary}
|
|
31
|
+
title="Plain Language Summary"
|
|
32
|
+
id="summary"
|
|
33
|
+
/>
|
|
34
|
+
<Keywords className={innerClassName} keywords={keywords} hideKeywords={hideKeywords} />
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { HashLink } from 'myst-to-react';
|
|
3
|
+
|
|
4
|
+
export function Keywords({
|
|
5
|
+
keywords,
|
|
6
|
+
hideKeywords,
|
|
7
|
+
className,
|
|
8
|
+
}: {
|
|
9
|
+
keywords?: string[];
|
|
10
|
+
hideKeywords?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
}) {
|
|
13
|
+
if (hideKeywords || !keywords || keywords.length === 0) return null;
|
|
14
|
+
return (
|
|
15
|
+
<div className={classNames('mb-10 group', className)}>
|
|
16
|
+
<span className="mr-2 font-semibold">Keywords:</span>
|
|
17
|
+
{keywords.map((k, i) => (
|
|
18
|
+
<span
|
|
19
|
+
key={k}
|
|
20
|
+
className={classNames({
|
|
21
|
+
"after:content-[','] after:mr-1": i < keywords.length - 1,
|
|
22
|
+
})}
|
|
23
|
+
>
|
|
24
|
+
{k}
|
|
25
|
+
</span>
|
|
26
|
+
))}
|
|
27
|
+
<HashLink id="keywords" title="Link to Keywords" hover className="ml-2" />
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Fragment } from 'react';
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import { Menu, Transition } from '@headlessui/react';
|
|
4
|
+
import { EllipsisVerticalIcon } from '@heroicons/react/24/solid';
|
|
5
|
+
import type { SiteManifest } from 'myst-config';
|
|
6
|
+
|
|
7
|
+
export function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
|
|
8
|
+
if (!actions || actions.length === 0) return null;
|
|
9
|
+
return (
|
|
10
|
+
<Menu as="div" className="relative">
|
|
11
|
+
<div>
|
|
12
|
+
<Menu.Button className="flex text-sm bg-transparent rounded-full focus:outline-none">
|
|
13
|
+
<span className="sr-only">Open Menu</span>
|
|
14
|
+
<div className="flex items-center text-stone-200 hover:text-white">
|
|
15
|
+
<EllipsisVerticalIcon width="2rem" height="2rem" className="p-1" />
|
|
16
|
+
</div>
|
|
17
|
+
</Menu.Button>
|
|
18
|
+
</div>
|
|
19
|
+
<Transition
|
|
20
|
+
as={Fragment}
|
|
21
|
+
enter="transition ease-out duration-100"
|
|
22
|
+
enterFrom="transform opacity-0 scale-95"
|
|
23
|
+
enterTo="transform opacity-100 scale-100"
|
|
24
|
+
leave="transition ease-in duration-75"
|
|
25
|
+
leaveFrom="transform opacity-100 scale-100"
|
|
26
|
+
leaveTo="transform opacity-0 scale-95"
|
|
27
|
+
>
|
|
28
|
+
<Menu.Items className="absolute right-0 w-48 py-1 mt-2 origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
29
|
+
{actions?.map((action) => (
|
|
30
|
+
<Menu.Item key={action.url}>
|
|
31
|
+
{({ active }) => (
|
|
32
|
+
<a
|
|
33
|
+
href={action.url}
|
|
34
|
+
className={classNames(
|
|
35
|
+
active ? 'bg-gray-100' : '',
|
|
36
|
+
'block px-4 py-2 text-sm text-gray-700',
|
|
37
|
+
)}
|
|
38
|
+
>
|
|
39
|
+
{action.title}
|
|
40
|
+
</a>
|
|
41
|
+
)}
|
|
42
|
+
</Menu.Item>
|
|
43
|
+
))}
|
|
44
|
+
</Menu.Items>
|
|
45
|
+
</Transition>
|
|
46
|
+
</Menu>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { useBaseurl, useLinkProvider, withBaseurl } from '@myst-theme/providers';
|
|
3
|
+
|
|
4
|
+
export function HomeLink({
|
|
5
|
+
logo,
|
|
6
|
+
logoDark,
|
|
7
|
+
logoText,
|
|
8
|
+
name,
|
|
9
|
+
}: {
|
|
10
|
+
logo?: string;
|
|
11
|
+
logoDark?: string;
|
|
12
|
+
logoText?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
}) {
|
|
15
|
+
const Link = useLinkProvider();
|
|
16
|
+
const baseurl = useBaseurl();
|
|
17
|
+
const nothingSet = !logo && !logoText;
|
|
18
|
+
return (
|
|
19
|
+
<Link
|
|
20
|
+
className="flex items-center ml-3 dark:text-white w-fit md:ml-5 xl:ml-7"
|
|
21
|
+
to={withBaseurl('/', baseurl)}
|
|
22
|
+
prefetch="intent"
|
|
23
|
+
>
|
|
24
|
+
{logo && (
|
|
25
|
+
<div
|
|
26
|
+
className={classNames('p-1 mr-3', {
|
|
27
|
+
'dark:bg-white dark:rounded': !logoDark,
|
|
28
|
+
})}
|
|
29
|
+
>
|
|
30
|
+
<img
|
|
31
|
+
src={logo}
|
|
32
|
+
className={classNames('h-9', { 'dark:hidden': !!logoDark })}
|
|
33
|
+
alt={logoText || name}
|
|
34
|
+
height="2.25rem"
|
|
35
|
+
></img>
|
|
36
|
+
{logoDark && (
|
|
37
|
+
<img
|
|
38
|
+
src={logoDark}
|
|
39
|
+
className="hidden h-9 dark:block"
|
|
40
|
+
alt={logoText || name}
|
|
41
|
+
height="2.25rem"
|
|
42
|
+
></img>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
)}
|
|
46
|
+
<span
|
|
47
|
+
className={classNames('text-md sm:text-xl tracking-tight sm:mr-5', {
|
|
48
|
+
'sr-only': !(logoText || nothingSet),
|
|
49
|
+
})}
|
|
50
|
+
>
|
|
51
|
+
{logoText || 'Made with MyST'}
|
|
52
|
+
</span>
|
|
53
|
+
</Link>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -5,7 +5,7 @@ import classNames from 'classnames';
|
|
|
5
5
|
/**
|
|
6
6
|
* Show a loading progess bad if the load takes more than 150ms
|
|
7
7
|
*/
|
|
8
|
-
function useLoading() {
|
|
8
|
+
export function useLoading() {
|
|
9
9
|
const transitionState = useNavigation().state;
|
|
10
10
|
const ref = useMemo<{ start?: NodeJS.Timeout; finish?: NodeJS.Timeout }>(() => ({}), []);
|
|
11
11
|
const [showLoading, setShowLoading] = useState(false);
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import { Fragment } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import { Menu, Transition } from '@headlessui/react';
|
|
4
|
-
import {
|
|
5
|
-
EllipsisVerticalIcon,
|
|
6
|
-
ChevronDownIcon,
|
|
7
|
-
Bars3Icon as MenuIcon,
|
|
8
|
-
} from '@heroicons/react/24/solid';
|
|
4
|
+
import { ChevronDownIcon, Bars3Icon as MenuIcon } from '@heroicons/react/24/solid';
|
|
9
5
|
import type { SiteManifest, SiteNavItem } from 'myst-config';
|
|
10
6
|
import { ThemeButton } from './ThemeButton.js';
|
|
11
7
|
import {
|
|
12
|
-
useBaseurl,
|
|
13
8
|
useLinkProvider,
|
|
14
9
|
useNavLinkProvider,
|
|
15
10
|
useNavOpen,
|
|
16
11
|
useSiteManifest,
|
|
17
|
-
withBaseurl,
|
|
18
12
|
} from '@myst-theme/providers';
|
|
19
13
|
import { LoadingBar } from './Loading.js';
|
|
14
|
+
import { HomeLink } from './HomeLink.js';
|
|
15
|
+
import { ActionMenu } from './ActionMenu.js';
|
|
20
16
|
|
|
21
17
|
export const DEFAULT_NAV_HEIGHT = 60;
|
|
22
18
|
|
|
@@ -57,7 +53,7 @@ function ExternalOrInternalLink({
|
|
|
57
53
|
);
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
function NavItem({ item }: { item: SiteNavItem }) {
|
|
56
|
+
export function NavItem({ item }: { item: SiteNavItem }) {
|
|
61
57
|
const NavLink = useNavLinkProvider();
|
|
62
58
|
if (!('children' in item)) {
|
|
63
59
|
return (
|
|
@@ -136,7 +132,7 @@ function NavItem({ item }: { item: SiteNavItem }) {
|
|
|
136
132
|
);
|
|
137
133
|
}
|
|
138
134
|
|
|
139
|
-
function NavItems({ nav }: { nav?: SiteManifest['nav'] }) {
|
|
135
|
+
export function NavItems({ nav }: { nav?: SiteManifest['nav'] }) {
|
|
140
136
|
if (!nav) return null;
|
|
141
137
|
return (
|
|
142
138
|
<div className="flex-grow hidden text-md lg:block">
|
|
@@ -147,102 +143,6 @@ function NavItems({ nav }: { nav?: SiteManifest['nav'] }) {
|
|
|
147
143
|
);
|
|
148
144
|
}
|
|
149
145
|
|
|
150
|
-
function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
|
|
151
|
-
if (!actions || actions.length === 0) return null;
|
|
152
|
-
return (
|
|
153
|
-
<Menu as="div" className="relative">
|
|
154
|
-
<div>
|
|
155
|
-
<Menu.Button className="flex text-sm bg-transparent rounded-full focus:outline-none">
|
|
156
|
-
<span className="sr-only">Open Menu</span>
|
|
157
|
-
<div className="flex items-center text-stone-200 hover:text-white">
|
|
158
|
-
<EllipsisVerticalIcon width="2rem" height="2rem" className="p-1" />
|
|
159
|
-
</div>
|
|
160
|
-
</Menu.Button>
|
|
161
|
-
</div>
|
|
162
|
-
<Transition
|
|
163
|
-
as={Fragment}
|
|
164
|
-
enter="transition ease-out duration-100"
|
|
165
|
-
enterFrom="transform opacity-0 scale-95"
|
|
166
|
-
enterTo="transform opacity-100 scale-100"
|
|
167
|
-
leave="transition ease-in duration-75"
|
|
168
|
-
leaveFrom="transform opacity-100 scale-100"
|
|
169
|
-
leaveTo="transform opacity-0 scale-95"
|
|
170
|
-
>
|
|
171
|
-
<Menu.Items className="absolute right-0 w-48 py-1 mt-2 origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
|
172
|
-
{actions?.map((action) => (
|
|
173
|
-
<Menu.Item key={action.url}>
|
|
174
|
-
{({ active }) => (
|
|
175
|
-
<a
|
|
176
|
-
href={action.url}
|
|
177
|
-
className={classNames(
|
|
178
|
-
active ? 'bg-gray-100' : '',
|
|
179
|
-
'block px-4 py-2 text-sm text-gray-700',
|
|
180
|
-
)}
|
|
181
|
-
>
|
|
182
|
-
{action.title}
|
|
183
|
-
</a>
|
|
184
|
-
)}
|
|
185
|
-
</Menu.Item>
|
|
186
|
-
))}
|
|
187
|
-
</Menu.Items>
|
|
188
|
-
</Transition>
|
|
189
|
-
</Menu>
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function HomeLink({
|
|
194
|
-
logo,
|
|
195
|
-
logoDark,
|
|
196
|
-
logoText,
|
|
197
|
-
name,
|
|
198
|
-
}: {
|
|
199
|
-
logo?: string;
|
|
200
|
-
logoDark?: string;
|
|
201
|
-
logoText?: string;
|
|
202
|
-
name?: string;
|
|
203
|
-
}) {
|
|
204
|
-
const Link = useLinkProvider();
|
|
205
|
-
const baseurl = useBaseurl();
|
|
206
|
-
const nothingSet = !logo && !logoText;
|
|
207
|
-
return (
|
|
208
|
-
<Link
|
|
209
|
-
className="flex items-center ml-3 dark:text-white w-fit md:ml-5 xl:ml-7"
|
|
210
|
-
to={withBaseurl('/', baseurl)}
|
|
211
|
-
prefetch="intent"
|
|
212
|
-
>
|
|
213
|
-
{logo && (
|
|
214
|
-
<div
|
|
215
|
-
className={classNames('p-1 mr-3', {
|
|
216
|
-
'dark:bg-white dark:rounded': !logoDark,
|
|
217
|
-
})}
|
|
218
|
-
>
|
|
219
|
-
<img
|
|
220
|
-
src={logo}
|
|
221
|
-
className={classNames('h-9', { 'dark:hidden': !!logoDark })}
|
|
222
|
-
alt={logoText || name}
|
|
223
|
-
height="2.25rem"
|
|
224
|
-
></img>
|
|
225
|
-
{logoDark && (
|
|
226
|
-
<img
|
|
227
|
-
src={logoDark}
|
|
228
|
-
className="hidden h-9 dark:block"
|
|
229
|
-
alt={logoText || name}
|
|
230
|
-
height="2.25rem"
|
|
231
|
-
></img>
|
|
232
|
-
)}
|
|
233
|
-
</div>
|
|
234
|
-
)}
|
|
235
|
-
<span
|
|
236
|
-
className={classNames('text-md sm:text-xl tracking-tight sm:mr-5', {
|
|
237
|
-
'sr-only': !(logoText || nothingSet),
|
|
238
|
-
})}
|
|
239
|
-
>
|
|
240
|
-
{logoText || 'Made with MyST'}
|
|
241
|
-
</span>
|
|
242
|
-
</Link>
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
146
|
export function TopNav() {
|
|
247
147
|
const [open, setOpen] = useNavOpen();
|
|
248
148
|
const config = useSiteManifest();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { ThemeButton } from './ThemeButton.js';
|
|
2
|
-
export { TopNav, DEFAULT_NAV_HEIGHT } from './TopNav.js';
|
|
2
|
+
export { TopNav, NavItems, NavItem, DEFAULT_NAV_HEIGHT } from './TopNav.js';
|
|
3
3
|
export { Navigation } from './Navigation.js';
|
|
4
4
|
export { TableOfContents, InlineTableOfContents, useTocHeight } from './TableOfContents.js';
|
|
5
5
|
export { LoadingBar } from './Loading.js';
|
|
6
|
+
export { ActionMenu } from './ActionMenu.js';
|
|
7
|
+
export { HomeLink } from './HomeLink.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback } from 'react';
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
function makeSkipClickHandler(hash: string) {
|
|
4
4
|
return (e: React.UIEvent<HTMLElement, Event>) => {
|
|
@@ -10,6 +10,10 @@ function makeSkipClickHandler(hash: string) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @deprecated use `SkipTo` instead with a list of targets
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
13
17
|
export function SkipToArticle({
|
|
14
18
|
frontmatter = true,
|
|
15
19
|
article = true,
|
|
@@ -48,3 +52,26 @@ export function SkipToArticle({
|
|
|
48
52
|
</div>
|
|
49
53
|
);
|
|
50
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Add a skip navigation unit with links based on a list of targets
|
|
58
|
+
*/
|
|
59
|
+
export const SkipTo = React.memo(({ targets }: { targets: { id: string; title: string }[] }) => {
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
className="fixed top-1 left-1 h-[0px] w-[0px] focus-within:z-40 focus-within:h-auto focus-within:w-auto bg-white overflow-hidden focus-within:p-2 focus-within:ring-1"
|
|
63
|
+
aria-label="skip to content options"
|
|
64
|
+
>
|
|
65
|
+
{targets.map(({ id, title }) => (
|
|
66
|
+
<a
|
|
67
|
+
key={id}
|
|
68
|
+
href={`#${id}`}
|
|
69
|
+
className="block px-2 py-1 text-black underline"
|
|
70
|
+
onClick={makeSkipClickHandler(id)}
|
|
71
|
+
>
|
|
72
|
+
{title}
|
|
73
|
+
</a>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
});
|
package/src/components/index.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
export { ContentBlocks } from './ContentBlocks.js';
|
|
2
|
-
export {
|
|
3
|
-
|
|
1
|
+
export { ContentBlocks, Block } from './ContentBlocks.js';
|
|
2
|
+
export {
|
|
3
|
+
DocumentOutline,
|
|
4
|
+
useOutlineHeight,
|
|
5
|
+
SupportingDocuments,
|
|
6
|
+
useHeaders,
|
|
7
|
+
} from './DocumentOutline.js';
|
|
8
|
+
export { FooterLinksBlock, FooterLink } from './FooterLinksBlock.js';
|
|
4
9
|
export { ContentReload } from './ContentReload.js';
|
|
5
10
|
export { Bibliography } from './Bibliography.js';
|
|
6
11
|
export { Footnotes } from './Footnotes.js';
|
|
7
12
|
export { ArticleHeader } from './Headers.js';
|
|
8
|
-
export {
|
|
13
|
+
export { Abstract } from './Abstract.js';
|
|
14
|
+
export { Keywords } from './Keywords.js';
|
|
15
|
+
export { FrontmatterParts } from './FrontmatterParts.js';
|
|
9
16
|
export { BackmatterParts, Backmatter } from './Backmatter.js';
|
|
10
17
|
export { ExternalOrInternalLink } from './ExternalOrInternalLink.js';
|
|
11
18
|
export * from './Navigation/index.js';
|
|
12
19
|
export { renderers } from './renderers.js';
|
|
13
|
-
export
|
|
20
|
+
export { SkipToArticle, SkipTo } from './SkipToArticle.js';
|
package/src/pages/Article.tsx
CHANGED
|
@@ -21,25 +21,7 @@ import {
|
|
|
21
21
|
useComputeOptions,
|
|
22
22
|
} from '@myst-theme/jupyter';
|
|
23
23
|
import { FrontmatterBlock } from '@myst-theme/frontmatter';
|
|
24
|
-
import { extractKnownParts } from '../utils.js';
|
|
25
|
-
import type { SiteAction } from 'myst-config';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Combines the project downloads and the export options
|
|
29
|
-
*/
|
|
30
|
-
function combineDownloads(
|
|
31
|
-
siteDownloads: SiteAction[] | undefined,
|
|
32
|
-
pageFrontmatter: PageLoader['frontmatter'],
|
|
33
|
-
) {
|
|
34
|
-
if (pageFrontmatter.downloads) {
|
|
35
|
-
return pageFrontmatter.downloads;
|
|
36
|
-
}
|
|
37
|
-
// No downloads on the page, combine the exports if they exist
|
|
38
|
-
if (siteDownloads) {
|
|
39
|
-
return [...(pageFrontmatter.exports ?? []), ...siteDownloads];
|
|
40
|
-
}
|
|
41
|
-
return pageFrontmatter.exports;
|
|
42
|
-
}
|
|
24
|
+
import { combineDownloads, extractKnownParts } from '../utils.js';
|
|
43
25
|
|
|
44
26
|
/**
|
|
45
27
|
* @deprecated This component is not maintained, in favor of theme-specific ArticlePages
|
package/src/seo/meta.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { HtmlMetaDescriptor, V2_MetaDescriptor } from '@remix-run/react';
|
|
2
2
|
|
|
3
|
-
type SocialSite = {
|
|
3
|
+
export type SocialSite = {
|
|
4
4
|
title: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
twitter?: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
type SocialArticle = {
|
|
9
|
+
export type SocialArticle = {
|
|
10
10
|
origin: string;
|
|
11
11
|
url: string;
|
|
12
12
|
// TODO: canonical
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { GenericParent } from 'myst-common';
|
|
1
|
+
import type { GenericNode, GenericParent } from 'myst-common';
|
|
2
2
|
import { extractPart } from 'myst-common';
|
|
3
|
+
import type { PageLoader } from '@myst-theme/common';
|
|
4
|
+
import type { SiteAction } from 'myst-config';
|
|
5
|
+
import { executableNodesFromBlock } from '@myst-theme/jupyter';
|
|
3
6
|
|
|
4
7
|
export function getDomainFromRequest(request: Request) {
|
|
5
8
|
const url = new URL(request.url);
|
|
@@ -23,3 +26,39 @@ export function extractKnownParts(tree: GenericParent): KnownParts {
|
|
|
23
26
|
const acknowledgments = extractPart(tree, ['acknowledgments', 'acknowledgements']);
|
|
24
27
|
return { abstract, summary, keypoints, data_availability, acknowledgments };
|
|
25
28
|
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Combines the project downloads and the export options
|
|
32
|
+
*/
|
|
33
|
+
export function combineDownloads(
|
|
34
|
+
siteDownloads: SiteAction[] | undefined,
|
|
35
|
+
pageFrontmatter: PageLoader['frontmatter'],
|
|
36
|
+
) {
|
|
37
|
+
if (pageFrontmatter.downloads) {
|
|
38
|
+
return pageFrontmatter.downloads;
|
|
39
|
+
}
|
|
40
|
+
// No downloads on the page, combine the exports if they exist
|
|
41
|
+
if (siteDownloads) {
|
|
42
|
+
return [...(pageFrontmatter.exports ?? []), ...siteDownloads];
|
|
43
|
+
}
|
|
44
|
+
return pageFrontmatter.exports;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* This returns the contents of a part that we want to render (not the root or block, which are already wrapped)
|
|
49
|
+
* This also fixes a bug that the key is not defined on a block.
|
|
50
|
+
*/
|
|
51
|
+
export function getChildren(content?: GenericParent): GenericNode | GenericNode[] {
|
|
52
|
+
if (
|
|
53
|
+
content?.type === 'root' &&
|
|
54
|
+
content.children?.length === 1 &&
|
|
55
|
+
content.children[0].type === 'block'
|
|
56
|
+
) {
|
|
57
|
+
return content.children[0].children as GenericNode[];
|
|
58
|
+
}
|
|
59
|
+
return content as GenericNode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isACodeCell(node: GenericParent) {
|
|
63
|
+
return !!executableNodesFromBlock(node);
|
|
64
|
+
}
|