@docusaurus/theme-classic 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/theme/BlogArchivePage/index.js +9 -1
- package/lib/theme/BlogLayout/index.js +1 -3
- package/lib/theme/BlogListPage/StructuredData/index.d.ts +10 -0
- package/lib/theme/BlogListPage/StructuredData/index.js +19 -0
- package/lib/theme/BlogListPage/index.js +2 -0
- package/lib/theme/BlogPostItem/Container/index.js +1 -26
- package/lib/theme/BlogPostItem/Content/index.js +1 -2
- package/lib/theme/BlogPostItem/Footer/index.js +61 -31
- package/lib/theme/BlogPostItem/Header/Author/index.js +5 -18
- package/lib/theme/BlogPostItem/Header/Info/index.js +15 -9
- package/lib/theme/BlogPostItem/Header/Title/index.js +2 -8
- package/lib/theme/{BlogPostItem/Footer/styles.module.css → BlogPostPage/StructuredData/index.d.ts} +2 -4
- package/lib/theme/BlogPostPage/StructuredData/index.js +19 -0
- package/lib/theme/BlogPostPage/index.js +3 -0
- package/lib/theme/DocCard/index.js +19 -12
- package/lib/theme/DocItem/Footer/index.js +17 -42
- package/lib/theme/DocSidebarItem/Category/index.js +3 -1
- package/lib/theme/EditMetaRow/index.d.ts +3 -0
- package/lib/theme/EditMetaRow/index.js +31 -0
- package/lib/theme/LastUpdated/index.d.ts +1 -1
- package/lib/theme/LastUpdated/index.js +17 -16
- package/lib/theme/MDXComponents/Img/index.js +1 -0
- package/{src/theme/BlogPostItem/Footer/styles.module.css → lib/theme/MDXComponents/Li.d.ts} +3 -4
- package/lib/theme/MDXComponents/Li.js +13 -0
- package/lib/theme/MDXComponents/index.js +2 -0
- package/lib/theme/NavbarItem/DropdownNavbarItem/index.js +3 -0
- package/package.json +14 -15
- package/src/theme/BlogArchivePage/index.tsx +11 -1
- package/src/theme/BlogLayout/index.tsx +1 -3
- package/src/theme/BlogListPage/StructuredData/index.tsx +22 -0
- package/src/theme/BlogListPage/index.tsx +2 -0
- package/src/theme/BlogPostItem/Container/index.tsx +1 -26
- package/src/theme/BlogPostItem/Content/index.tsx +1 -2
- package/src/theme/BlogPostItem/Footer/index.tsx +62 -32
- package/src/theme/BlogPostItem/Header/Author/index.tsx +5 -18
- package/src/theme/BlogPostItem/Header/Info/index.tsx +24 -9
- package/src/theme/BlogPostItem/Header/Title/index.tsx +2 -8
- package/src/theme/BlogPostPage/StructuredData/index.tsx +21 -0
- package/src/theme/BlogPostPage/index.tsx +3 -0
- package/src/theme/DocCard/index.tsx +20 -12
- package/src/theme/DocItem/Footer/index.tsx +19 -52
- package/src/theme/DocSidebarItem/Category/index.tsx +3 -1
- package/src/theme/EditMetaRow/index.tsx +34 -0
- package/src/theme/LastUpdated/index.tsx +18 -13
- package/src/theme/MDXComponents/Img/index.tsx +1 -0
- package/src/theme/MDXComponents/Li.tsx +17 -0
- package/src/theme/MDXComponents/index.tsx +2 -0
- package/src/theme/NavbarItem/DropdownNavbarItem/index.tsx +3 -0
- package/src/theme-classic.d.ts +18 -1
- package/lib/theme/{DocItem/Footer → EditMetaRow}/styles.module.css +2 -2
- package/src/theme/{DocItem/Footer → EditMetaRow}/styles.module.css +2 -2
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
7
|
+
import { type ReactNode } from 'react';
|
|
8
|
+
import type { Props } from '@theme/MDXComponents/Li';
|
|
9
|
+
export default function MDXLi(props: Props): ReactNode | undefined;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import useBrokenLinks from '@docusaurus/useBrokenLinks';
|
|
9
|
+
export default function MDXLi(props) {
|
|
10
|
+
// MDX Footnotes have ids such as <li id="user-content-fn-1-953011">
|
|
11
|
+
useBrokenLinks().collectAnchor(props.id);
|
|
12
|
+
return <li {...props} />;
|
|
13
|
+
}
|
|
@@ -12,6 +12,7 @@ import MDXPre from '@theme/MDXComponents/Pre';
|
|
|
12
12
|
import MDXDetails from '@theme/MDXComponents/Details';
|
|
13
13
|
import MDXHeading from '@theme/MDXComponents/Heading';
|
|
14
14
|
import MDXUl from '@theme/MDXComponents/Ul';
|
|
15
|
+
import MDXLi from '@theme/MDXComponents/Li';
|
|
15
16
|
import MDXImg from '@theme/MDXComponents/Img';
|
|
16
17
|
import Admonition from '@theme/Admonition';
|
|
17
18
|
import Mermaid from '@theme/Mermaid';
|
|
@@ -23,6 +24,7 @@ const MDXComponents = {
|
|
|
23
24
|
a: MDXA,
|
|
24
25
|
pre: MDXPre,
|
|
25
26
|
ul: MDXUl,
|
|
27
|
+
li: MDXLi,
|
|
26
28
|
img: MDXImg,
|
|
27
29
|
h1: (props) => <MDXHeading as="h1" {...props} />,
|
|
28
30
|
h2: (props) => <MDXHeading as="h2" {...props} />,
|
|
@@ -66,6 +66,9 @@ function DropdownNavbarItemDesktop({
|
|
|
66
66
|
aria-haspopup="true"
|
|
67
67
|
aria-expanded={showDropdown}
|
|
68
68
|
role="button"
|
|
69
|
+
// # hash permits to make the <a> tag focusable in case no link target
|
|
70
|
+
// See https://github.com/facebook/docusaurus/pull/6003
|
|
71
|
+
// There's probably a better solution though...
|
|
69
72
|
href={props.to ? undefined : '#'}
|
|
70
73
|
className={clsx('navbar__link', className)}
|
|
71
74
|
{...props}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-classic",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Classic theme for Docusaurus",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "src/theme-classic.d.ts",
|
|
@@ -20,18 +20,18 @@
|
|
|
20
20
|
"copy:watch": "node ../../admin/scripts/copyUntypedFiles.js --watch"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@docusaurus/core": "3.
|
|
24
|
-
"@docusaurus/mdx-loader": "3.
|
|
25
|
-
"@docusaurus/module-type-aliases": "3.
|
|
26
|
-
"@docusaurus/plugin-content-blog": "3.
|
|
27
|
-
"@docusaurus/plugin-content-docs": "3.
|
|
28
|
-
"@docusaurus/plugin-content-pages": "3.
|
|
29
|
-
"@docusaurus/theme-common": "3.
|
|
30
|
-
"@docusaurus/theme-translations": "3.
|
|
31
|
-
"@docusaurus/types": "3.
|
|
32
|
-
"@docusaurus/utils": "3.
|
|
33
|
-
"@docusaurus/utils-common": "3.
|
|
34
|
-
"@docusaurus/utils-validation": "3.
|
|
23
|
+
"@docusaurus/core": "3.2.0",
|
|
24
|
+
"@docusaurus/mdx-loader": "3.2.0",
|
|
25
|
+
"@docusaurus/module-type-aliases": "3.2.0",
|
|
26
|
+
"@docusaurus/plugin-content-blog": "3.2.0",
|
|
27
|
+
"@docusaurus/plugin-content-docs": "3.2.0",
|
|
28
|
+
"@docusaurus/plugin-content-pages": "3.2.0",
|
|
29
|
+
"@docusaurus/theme-common": "3.2.0",
|
|
30
|
+
"@docusaurus/theme-translations": "3.2.0",
|
|
31
|
+
"@docusaurus/types": "3.2.0",
|
|
32
|
+
"@docusaurus/utils": "3.2.0",
|
|
33
|
+
"@docusaurus/utils-common": "3.2.0",
|
|
34
|
+
"@docusaurus/utils-validation": "3.2.0",
|
|
35
35
|
"@mdx-js/react": "^3.0.0",
|
|
36
36
|
"clsx": "^2.0.0",
|
|
37
37
|
"copy-text-to-clipboard": "^3.2.0",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"utility-types": "^3.10.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@types/mdx-js__react": "^1.5.5",
|
|
51
50
|
"@types/nprogress": "^0.2.0",
|
|
52
51
|
"@types/prismjs": "^1.26.0",
|
|
53
52
|
"@types/rtlcss": "^3.5.1",
|
|
@@ -62,5 +61,5 @@
|
|
|
62
61
|
"engines": {
|
|
63
62
|
"node": ">=18.0"
|
|
64
63
|
},
|
|
65
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "5af143651b26b39761361acd96e9c5be7ba0cb25"
|
|
66
65
|
}
|
|
@@ -9,6 +9,7 @@ import React from 'react';
|
|
|
9
9
|
import Link from '@docusaurus/Link';
|
|
10
10
|
import {translate} from '@docusaurus/Translate';
|
|
11
11
|
import {PageMetadata} from '@docusaurus/theme-common';
|
|
12
|
+
import {useDateTimeFormat} from '@docusaurus/theme-common/internal';
|
|
12
13
|
import Layout from '@theme/Layout';
|
|
13
14
|
import type {ArchiveBlogPost, Props} from '@theme/BlogArchivePage';
|
|
14
15
|
import Heading from '@theme/Heading';
|
|
@@ -19,6 +20,15 @@ type YearProp = {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
function Year({year, posts}: YearProp) {
|
|
23
|
+
const dateTimeFormat = useDateTimeFormat({
|
|
24
|
+
day: 'numeric',
|
|
25
|
+
month: 'long',
|
|
26
|
+
timeZone: 'UTC',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const formatDate = (lastUpdated: string) =>
|
|
30
|
+
dateTimeFormat.format(new Date(lastUpdated));
|
|
31
|
+
|
|
22
32
|
return (
|
|
23
33
|
<>
|
|
24
34
|
<Heading as="h3" id={year}>
|
|
@@ -28,7 +38,7 @@ function Year({year, posts}: YearProp) {
|
|
|
28
38
|
{posts.map((post) => (
|
|
29
39
|
<li key={post.metadata.date}>
|
|
30
40
|
<Link to={post.metadata.permalink}>
|
|
31
|
-
{post.metadata.
|
|
41
|
+
{formatDate(post.metadata.date)} - {post.metadata.title}
|
|
32
42
|
</Link>
|
|
33
43
|
</li>
|
|
34
44
|
))}
|
|
@@ -25,9 +25,7 @@ export default function BlogLayout(props: Props): JSX.Element {
|
|
|
25
25
|
className={clsx('col', {
|
|
26
26
|
'col--7': hasSidebar,
|
|
27
27
|
'col--9 col--offset-1': !hasSidebar,
|
|
28
|
-
})}
|
|
29
|
-
itemScope
|
|
30
|
-
itemType="https://schema.org/Blog">
|
|
28
|
+
})}>
|
|
31
29
|
{children}
|
|
32
30
|
</main>
|
|
33
31
|
{toc && <div className="col col--2">{toc}</div>}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import Head from '@docusaurus/Head';
|
|
10
|
+
import {useBlogListPageStructuredData} from '@docusaurus/theme-common';
|
|
11
|
+
import type {Props} from '@theme/BlogListPage/StructuredData';
|
|
12
|
+
|
|
13
|
+
export default function BlogListPageStructuredData(props: Props): JSX.Element {
|
|
14
|
+
const structuredData = useBlogListPageStructuredData(props);
|
|
15
|
+
return (
|
|
16
|
+
<Head>
|
|
17
|
+
<script type="application/ld+json">
|
|
18
|
+
{JSON.stringify(structuredData)}
|
|
19
|
+
</script>
|
|
20
|
+
</Head>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -19,6 +19,7 @@ import BlogListPaginator from '@theme/BlogListPaginator';
|
|
|
19
19
|
import SearchMetadata from '@theme/SearchMetadata';
|
|
20
20
|
import type {Props} from '@theme/BlogListPage';
|
|
21
21
|
import BlogPostItems from '@theme/BlogPostItems';
|
|
22
|
+
import BlogListPageStructuredData from '@theme/BlogListPage/StructuredData';
|
|
22
23
|
|
|
23
24
|
function BlogListPageMetadata(props: Props): JSX.Element {
|
|
24
25
|
const {metadata} = props;
|
|
@@ -54,6 +55,7 @@ export default function BlogListPage(props: Props): JSX.Element {
|
|
|
54
55
|
ThemeClassNames.page.blogListPage,
|
|
55
56
|
)}>
|
|
56
57
|
<BlogListPageMetadata {...props} />
|
|
58
|
+
<BlogListPageStructuredData {...props} />
|
|
57
59
|
<BlogListPageContent {...props} />
|
|
58
60
|
</HtmlClassNameProvider>
|
|
59
61
|
);
|
|
@@ -6,36 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React from 'react';
|
|
9
|
-
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
|
10
|
-
import {useBlogPost} from '@docusaurus/theme-common/internal';
|
|
11
9
|
import type {Props} from '@theme/BlogPostItem/Container';
|
|
12
10
|
|
|
13
11
|
export default function BlogPostItemContainer({
|
|
14
12
|
children,
|
|
15
13
|
className,
|
|
16
14
|
}: Props): JSX.Element {
|
|
17
|
-
|
|
18
|
-
frontMatter,
|
|
19
|
-
assets,
|
|
20
|
-
metadata: {description},
|
|
21
|
-
} = useBlogPost();
|
|
22
|
-
const {withBaseUrl} = useBaseUrlUtils();
|
|
23
|
-
const image = assets.image ?? frontMatter.image;
|
|
24
|
-
const keywords = frontMatter.keywords ?? [];
|
|
25
|
-
return (
|
|
26
|
-
<article
|
|
27
|
-
className={className}
|
|
28
|
-
itemProp="blogPost"
|
|
29
|
-
itemScope
|
|
30
|
-
itemType="https://schema.org/BlogPosting">
|
|
31
|
-
{description && <meta itemProp="description" content={description} />}
|
|
32
|
-
{image && (
|
|
33
|
-
<link itemProp="image" href={withBaseUrl(image, {absolute: true})} />
|
|
34
|
-
)}
|
|
35
|
-
{keywords.length > 0 && (
|
|
36
|
-
<meta itemProp="keywords" content={keywords.join(',')} />
|
|
37
|
-
)}
|
|
38
|
-
{children}
|
|
39
|
-
</article>
|
|
40
|
-
);
|
|
15
|
+
return <article className={className}>{children}</article>;
|
|
41
16
|
}
|
|
@@ -21,8 +21,7 @@ export default function BlogPostItemContent({
|
|
|
21
21
|
<div
|
|
22
22
|
// This ID is used for the feed generation to locate the main content
|
|
23
23
|
id={isBlogPostPage ? blogPostContainerID : undefined}
|
|
24
|
-
className={clsx('markdown', className)}
|
|
25
|
-
itemProp="articleBody">
|
|
24
|
+
className={clsx('markdown', className)}>
|
|
26
25
|
<MDXContent>{children}</MDXContent>
|
|
27
26
|
</div>
|
|
28
27
|
);
|
|
@@ -8,15 +8,21 @@
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import clsx from 'clsx';
|
|
10
10
|
import {useBlogPost} from '@docusaurus/theme-common/internal';
|
|
11
|
-
import
|
|
11
|
+
import {ThemeClassNames} from '@docusaurus/theme-common';
|
|
12
|
+
import EditMetaRow from '@theme/EditMetaRow';
|
|
12
13
|
import TagsListInline from '@theme/TagsListInline';
|
|
13
14
|
import ReadMoreLink from '@theme/BlogPostItem/Footer/ReadMoreLink';
|
|
14
15
|
|
|
15
|
-
import styles from './styles.module.css';
|
|
16
|
-
|
|
17
16
|
export default function BlogPostItemFooter(): JSX.Element | null {
|
|
18
17
|
const {metadata, isBlogPostPage} = useBlogPost();
|
|
19
|
-
const {
|
|
18
|
+
const {
|
|
19
|
+
tags,
|
|
20
|
+
title,
|
|
21
|
+
editUrl,
|
|
22
|
+
hasTruncateMarker,
|
|
23
|
+
lastUpdatedBy,
|
|
24
|
+
lastUpdatedAt,
|
|
25
|
+
} = metadata;
|
|
20
26
|
|
|
21
27
|
// A post is truncated if it's in the "list view" and it has a truncate marker
|
|
22
28
|
const truncatedPost = !isBlogPostPage && hasTruncateMarker;
|
|
@@ -29,32 +35,56 @@ export default function BlogPostItemFooter(): JSX.Element | null {
|
|
|
29
35
|
return null;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
38
|
+
// BlogPost footer - details view
|
|
39
|
+
if (isBlogPostPage) {
|
|
40
|
+
const canDisplayEditMetaRow = !!(editUrl || lastUpdatedAt || lastUpdatedBy);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<footer className="docusaurus-mt-lg">
|
|
44
|
+
{tagsExists && (
|
|
45
|
+
<div
|
|
46
|
+
className={clsx(
|
|
47
|
+
'row',
|
|
48
|
+
'margin-top--sm',
|
|
49
|
+
ThemeClassNames.blog.blogFooterEditMetaRow,
|
|
50
|
+
)}>
|
|
51
|
+
<div className="col">
|
|
52
|
+
<TagsListInline tags={tags} />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
)}
|
|
56
|
+
{canDisplayEditMetaRow && (
|
|
57
|
+
<EditMetaRow
|
|
58
|
+
className={clsx(
|
|
59
|
+
'margin-top--sm',
|
|
60
|
+
ThemeClassNames.blog.blogFooterEditMetaRow,
|
|
61
|
+
)}
|
|
62
|
+
editUrl={editUrl}
|
|
63
|
+
lastUpdatedAt={lastUpdatedAt}
|
|
64
|
+
lastUpdatedBy={lastUpdatedBy}
|
|
65
|
+
/>
|
|
66
|
+
)}
|
|
67
|
+
</footer>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
// BlogPost footer - list view
|
|
71
|
+
else {
|
|
72
|
+
return (
|
|
73
|
+
<footer className="row docusaurus-mt-lg">
|
|
74
|
+
{tagsExists && (
|
|
75
|
+
<div className={clsx('col', {'col--9': truncatedPost})}>
|
|
76
|
+
<TagsListInline tags={tags} />
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
{truncatedPost && (
|
|
80
|
+
<div
|
|
81
|
+
className={clsx('col text--right', {
|
|
82
|
+
'col--3': tagsExists,
|
|
83
|
+
})}>
|
|
84
|
+
<ReadMoreLink blogPostTitle={title} to={metadata.permalink} />
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
</footer>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
60
90
|
}
|
|
@@ -28,31 +28,18 @@ export default function BlogPostItemHeaderAuthor({
|
|
|
28
28
|
<div className={clsx('avatar margin-bottom--sm', className)}>
|
|
29
29
|
{imageURL && (
|
|
30
30
|
<MaybeLink href={link} className="avatar__photo-link">
|
|
31
|
-
<img
|
|
32
|
-
className="avatar__photo"
|
|
33
|
-
src={imageURL}
|
|
34
|
-
alt={name}
|
|
35
|
-
itemProp="image"
|
|
36
|
-
/>
|
|
31
|
+
<img className="avatar__photo" src={imageURL} alt={name} />
|
|
37
32
|
</MaybeLink>
|
|
38
33
|
)}
|
|
39
34
|
|
|
40
35
|
{name && (
|
|
41
|
-
<div
|
|
42
|
-
className="avatar__intro"
|
|
43
|
-
itemProp="author"
|
|
44
|
-
itemScope
|
|
45
|
-
itemType="https://schema.org/Person">
|
|
36
|
+
<div className="avatar__intro">
|
|
46
37
|
<div className="avatar__name">
|
|
47
|
-
<MaybeLink href={link}
|
|
48
|
-
<span
|
|
38
|
+
<MaybeLink href={link}>
|
|
39
|
+
<span>{name}</span>
|
|
49
40
|
</MaybeLink>
|
|
50
41
|
</div>
|
|
51
|
-
{title &&
|
|
52
|
-
<small className="avatar__subtitle" itemProp="description">
|
|
53
|
-
{title}
|
|
54
|
-
</small>
|
|
55
|
-
)}
|
|
42
|
+
{title && <small className="avatar__subtitle">{title}</small>}
|
|
56
43
|
</div>
|
|
57
44
|
)}
|
|
58
45
|
</div>
|
|
@@ -9,7 +9,10 @@ import React from 'react';
|
|
|
9
9
|
import clsx from 'clsx';
|
|
10
10
|
import {translate} from '@docusaurus/Translate';
|
|
11
11
|
import {usePluralForm} from '@docusaurus/theme-common';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
useBlogPost,
|
|
14
|
+
useDateTimeFormat,
|
|
15
|
+
} from '@docusaurus/theme-common/internal';
|
|
13
16
|
import type {Props} from '@theme/BlogPostItem/Header/Info';
|
|
14
17
|
|
|
15
18
|
import styles from './styles.module.css';
|
|
@@ -39,12 +42,14 @@ function ReadingTime({readingTime}: {readingTime: number}) {
|
|
|
39
42
|
return <>{readingTimePlural(readingTime)}</>;
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
function DateTime({
|
|
46
|
+
date,
|
|
47
|
+
formattedDate,
|
|
48
|
+
}: {
|
|
49
|
+
date: string;
|
|
50
|
+
formattedDate: string;
|
|
51
|
+
}) {
|
|
52
|
+
return <time dateTime={date}>{formattedDate}</time>;
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
function Spacer() {
|
|
@@ -55,11 +60,21 @@ export default function BlogPostItemHeaderInfo({
|
|
|
55
60
|
className,
|
|
56
61
|
}: Props): JSX.Element {
|
|
57
62
|
const {metadata} = useBlogPost();
|
|
58
|
-
const {date,
|
|
63
|
+
const {date, readingTime} = metadata;
|
|
64
|
+
|
|
65
|
+
const dateTimeFormat = useDateTimeFormat({
|
|
66
|
+
day: 'numeric',
|
|
67
|
+
month: 'long',
|
|
68
|
+
year: 'numeric',
|
|
69
|
+
timeZone: 'UTC',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const formatDate = (blogDate: string) =>
|
|
73
|
+
dateTimeFormat.format(new Date(blogDate));
|
|
59
74
|
|
|
60
75
|
return (
|
|
61
76
|
<div className={clsx(styles.container, 'margin-vert--md', className)}>
|
|
62
|
-
<
|
|
77
|
+
<DateTime date={date} formattedDate={formatDate(date)} />
|
|
63
78
|
{typeof readingTime !== 'undefined' && (
|
|
64
79
|
<>
|
|
65
80
|
<Spacer />
|
|
@@ -20,14 +20,8 @@ export default function BlogPostItemHeaderTitle({
|
|
|
20
20
|
const {permalink, title} = metadata;
|
|
21
21
|
const TitleHeading = isBlogPostPage ? 'h1' : 'h2';
|
|
22
22
|
return (
|
|
23
|
-
<TitleHeading className={clsx(styles.title, className)}
|
|
24
|
-
{isBlogPostPage ?
|
|
25
|
-
title
|
|
26
|
-
) : (
|
|
27
|
-
<Link itemProp="url" to={permalink}>
|
|
28
|
-
{title}
|
|
29
|
-
</Link>
|
|
30
|
-
)}
|
|
23
|
+
<TitleHeading className={clsx(styles.title, className)}>
|
|
24
|
+
{isBlogPostPage ? title : <Link to={permalink}>{title}</Link>}
|
|
31
25
|
</TitleHeading>
|
|
32
26
|
);
|
|
33
27
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import Head from '@docusaurus/Head';
|
|
10
|
+
import {useBlogPostStructuredData} from '@docusaurus/theme-common';
|
|
11
|
+
|
|
12
|
+
export default function BlogPostStructuredData(): JSX.Element {
|
|
13
|
+
const structuredData = useBlogPostStructuredData();
|
|
14
|
+
return (
|
|
15
|
+
<Head>
|
|
16
|
+
<script type="application/ld+json">
|
|
17
|
+
{JSON.stringify(structuredData)}
|
|
18
|
+
</script>
|
|
19
|
+
</Head>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -13,6 +13,7 @@ import BlogLayout from '@theme/BlogLayout';
|
|
|
13
13
|
import BlogPostItem from '@theme/BlogPostItem';
|
|
14
14
|
import BlogPostPaginator from '@theme/BlogPostPaginator';
|
|
15
15
|
import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata';
|
|
16
|
+
import BlogPostPageStructuredData from '@theme/BlogPostPage/StructuredData';
|
|
16
17
|
import TOC from '@theme/TOC';
|
|
17
18
|
import type {Props} from '@theme/BlogPostPage';
|
|
18
19
|
import Unlisted from '@theme/Unlisted';
|
|
@@ -45,6 +46,7 @@ function BlogPostPageContent({
|
|
|
45
46
|
) : undefined
|
|
46
47
|
}>
|
|
47
48
|
{unlisted && <Unlisted />}
|
|
49
|
+
|
|
48
50
|
<BlogPostItem>{children}</BlogPostItem>
|
|
49
51
|
|
|
50
52
|
{(nextItem || prevItem) && (
|
|
@@ -64,6 +66,7 @@ export default function BlogPostPage(props: Props): JSX.Element {
|
|
|
64
66
|
ThemeClassNames.page.blogPostPage,
|
|
65
67
|
)}>
|
|
66
68
|
<BlogPostPageMetadata />
|
|
69
|
+
<BlogPostPageStructuredData />
|
|
67
70
|
<BlogPostPageContent sidebar={props.sidebar}>
|
|
68
71
|
<BlogPostContent />
|
|
69
72
|
</BlogPostPageContent>
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
findFirstSidebarItemLink,
|
|
13
13
|
useDocById,
|
|
14
14
|
} from '@docusaurus/theme-common/internal';
|
|
15
|
+
import {usePluralForm} from '@docusaurus/theme-common';
|
|
15
16
|
import isInternalUrl from '@docusaurus/isInternalUrl';
|
|
16
17
|
import {translate} from '@docusaurus/Translate';
|
|
17
18
|
|
|
@@ -24,6 +25,23 @@ import type {
|
|
|
24
25
|
|
|
25
26
|
import styles from './styles.module.css';
|
|
26
27
|
|
|
28
|
+
function useCategoryItemsPlural() {
|
|
29
|
+
const {selectMessage} = usePluralForm();
|
|
30
|
+
return (count: number) =>
|
|
31
|
+
selectMessage(
|
|
32
|
+
count,
|
|
33
|
+
translate(
|
|
34
|
+
{
|
|
35
|
+
message: '{count} items',
|
|
36
|
+
id: 'theme.docs.DocCard.categoryDescription.plurals',
|
|
37
|
+
description:
|
|
38
|
+
'The default description for a category card in the generated index about how many items this category includes',
|
|
39
|
+
},
|
|
40
|
+
{count},
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
27
45
|
function CardContainer({
|
|
28
46
|
href,
|
|
29
47
|
children,
|
|
@@ -76,6 +94,7 @@ function CardCategory({
|
|
|
76
94
|
item: PropSidebarItemCategory;
|
|
77
95
|
}): JSX.Element | null {
|
|
78
96
|
const href = findFirstSidebarItemLink(item);
|
|
97
|
+
const categoryItemsPlural = useCategoryItemsPlural();
|
|
79
98
|
|
|
80
99
|
// Unexpected: categories that don't have a link have been filtered upfront
|
|
81
100
|
if (!href) {
|
|
@@ -87,18 +106,7 @@ function CardCategory({
|
|
|
87
106
|
href={href}
|
|
88
107
|
icon="🗃️"
|
|
89
108
|
title={item.label}
|
|
90
|
-
description={
|
|
91
|
-
item.description ??
|
|
92
|
-
translate(
|
|
93
|
-
{
|
|
94
|
-
message: '{count} items',
|
|
95
|
-
id: 'theme.docs.DocCard.categoryDescription',
|
|
96
|
-
description:
|
|
97
|
-
'The default description for a category card in the generated index about how many items this category includes',
|
|
98
|
-
},
|
|
99
|
-
{count: item.items.length},
|
|
100
|
-
)
|
|
101
|
-
}
|
|
109
|
+
description={item.description ?? categoryItemsPlural(item.items.length)}
|
|
102
110
|
/>
|
|
103
111
|
);
|
|
104
112
|
}
|
|
@@ -8,60 +8,14 @@
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import clsx from 'clsx';
|
|
10
10
|
import {ThemeClassNames} from '@docusaurus/theme-common';
|
|
11
|
-
import {useDoc
|
|
12
|
-
import
|
|
13
|
-
import EditThisPage from '@theme/EditThisPage';
|
|
14
|
-
import TagsListInline, {
|
|
15
|
-
type Props as TagsListInlineProps,
|
|
16
|
-
} from '@theme/TagsListInline';
|
|
11
|
+
import {useDoc} from '@docusaurus/theme-common/internal';
|
|
12
|
+
import TagsListInline from '@theme/TagsListInline';
|
|
17
13
|
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
function TagsRow(props: TagsListInlineProps) {
|
|
21
|
-
return (
|
|
22
|
-
<div
|
|
23
|
-
className={clsx(
|
|
24
|
-
ThemeClassNames.docs.docFooterTagsRow,
|
|
25
|
-
'row margin-bottom--sm',
|
|
26
|
-
)}>
|
|
27
|
-
<div className="col">
|
|
28
|
-
<TagsListInline {...props} />
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
type EditMetaRowProps = Pick<
|
|
35
|
-
DocContextValue['metadata'],
|
|
36
|
-
'editUrl' | 'lastUpdatedAt' | 'lastUpdatedBy' | 'formattedLastUpdatedAt'
|
|
37
|
-
>;
|
|
38
|
-
function EditMetaRow({
|
|
39
|
-
editUrl,
|
|
40
|
-
lastUpdatedAt,
|
|
41
|
-
lastUpdatedBy,
|
|
42
|
-
formattedLastUpdatedAt,
|
|
43
|
-
}: EditMetaRowProps) {
|
|
44
|
-
return (
|
|
45
|
-
<div className={clsx(ThemeClassNames.docs.docFooterEditMetaRow, 'row')}>
|
|
46
|
-
<div className="col">{editUrl && <EditThisPage editUrl={editUrl} />}</div>
|
|
47
|
-
|
|
48
|
-
<div className={clsx('col', styles.lastUpdated)}>
|
|
49
|
-
{(lastUpdatedAt || lastUpdatedBy) && (
|
|
50
|
-
<LastUpdated
|
|
51
|
-
lastUpdatedAt={lastUpdatedAt}
|
|
52
|
-
formattedLastUpdatedAt={formattedLastUpdatedAt}
|
|
53
|
-
lastUpdatedBy={lastUpdatedBy}
|
|
54
|
-
/>
|
|
55
|
-
)}
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
14
|
+
import EditMetaRow from '@theme/EditMetaRow';
|
|
60
15
|
|
|
61
16
|
export default function DocItemFooter(): JSX.Element | null {
|
|
62
17
|
const {metadata} = useDoc();
|
|
63
|
-
const {editUrl, lastUpdatedAt,
|
|
64
|
-
metadata;
|
|
18
|
+
const {editUrl, lastUpdatedAt, lastUpdatedBy, tags} = metadata;
|
|
65
19
|
|
|
66
20
|
const canDisplayTagsRow = tags.length > 0;
|
|
67
21
|
const canDisplayEditMetaRow = !!(editUrl || lastUpdatedAt || lastUpdatedBy);
|
|
@@ -75,13 +29,26 @@ export default function DocItemFooter(): JSX.Element | null {
|
|
|
75
29
|
return (
|
|
76
30
|
<footer
|
|
77
31
|
className={clsx(ThemeClassNames.docs.docFooter, 'docusaurus-mt-lg')}>
|
|
78
|
-
{canDisplayTagsRow &&
|
|
32
|
+
{canDisplayTagsRow && (
|
|
33
|
+
<div
|
|
34
|
+
className={clsx(
|
|
35
|
+
'row margin-top--sm',
|
|
36
|
+
ThemeClassNames.docs.docFooterTagsRow,
|
|
37
|
+
)}>
|
|
38
|
+
<div className="col">
|
|
39
|
+
<TagsListInline tags={tags} />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
)}
|
|
79
43
|
{canDisplayEditMetaRow && (
|
|
80
44
|
<EditMetaRow
|
|
45
|
+
className={clsx(
|
|
46
|
+
'margin-top--sm',
|
|
47
|
+
ThemeClassNames.docs.docFooterEditMetaRow,
|
|
48
|
+
)}
|
|
81
49
|
editUrl={editUrl}
|
|
82
50
|
lastUpdatedAt={lastUpdatedAt}
|
|
83
51
|
lastUpdatedBy={lastUpdatedBy}
|
|
84
|
-
formattedLastUpdatedAt={formattedLastUpdatedAt}
|
|
85
52
|
/>
|
|
86
53
|
)}
|
|
87
54
|
</footer>
|