@docusaurus/theme-classic 2.0.0-beta.17 → 2.0.0-beta.18

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.
Files changed (184) hide show
  1. package/lib/getSwizzleConfig.js +113 -2
  2. package/lib/index.js +7 -12
  3. package/lib/theme-classic.d.ts +286 -60
  4. package/lib/translations.js +12 -2
  5. package/lib/validateThemeConfig.js +1 -1
  6. package/lib-next/theme/BackToTopButton/index.js +1 -3
  7. package/lib-next/theme/BlogArchivePage/index.js +14 -10
  8. package/lib-next/theme/BlogListPage/index.js +34 -13
  9. package/lib-next/theme/BlogPostItem/index.js +6 -6
  10. package/lib-next/theme/BlogPostPage/index.js +56 -45
  11. package/lib-next/theme/BlogTagsListPage/index.js +16 -12
  12. package/lib-next/theme/BlogTagsPostsPage/index.js +40 -33
  13. package/lib-next/theme/CodeBlock/CopyButton/index.js +60 -0
  14. package/lib-next/theme/CodeBlock/CopyButton/styles.module.css +66 -0
  15. package/lib-next/theme/CodeBlock/index.js +4 -36
  16. package/lib-next/theme/CodeBlock/styles.module.css +2 -22
  17. package/lib-next/theme/ColorModeToggle/index.js +36 -57
  18. package/lib-next/theme/ColorModeToggle/styles.module.css +8 -24
  19. package/lib-next/theme/DocBreadcrumbs/index.js +28 -11
  20. package/lib-next/theme/DocBreadcrumbs/styles.module.css +2 -17
  21. package/lib-next/theme/DocCard/index.js +17 -15
  22. package/lib-next/theme/DocCard/styles.module.css +8 -17
  23. package/lib-next/theme/DocCardList/index.js +15 -3
  24. package/lib-next/theme/DocCategoryGeneratedIndexPage/index.js +27 -6
  25. package/lib-next/theme/DocItem/index.js +84 -66
  26. package/lib-next/theme/DocPage/index.js +88 -89
  27. package/lib-next/theme/DocPage/styles.module.css +0 -4
  28. package/lib-next/theme/DocSidebar/Desktop/CollapseButton/index.js +34 -0
  29. package/lib-next/theme/DocSidebar/Desktop/CollapseButton/styles.module.css +44 -0
  30. package/lib-next/theme/DocSidebar/Desktop/Content/index.js +46 -0
  31. package/lib-next/theme/DocSidebar/Desktop/Content/styles.module.css +17 -0
  32. package/lib-next/theme/DocSidebar/Desktop/index.js +34 -0
  33. package/lib-next/theme/DocSidebar/Desktop/styles.module.css +50 -0
  34. package/lib-next/theme/DocSidebar/Mobile/index.js +49 -0
  35. package/lib-next/theme/DocSidebar/index.js +6 -113
  36. package/lib-next/theme/DocSidebarItem/index.js +14 -13
  37. package/lib-next/theme/DocSidebarItem/styles.module.css +4 -0
  38. package/lib-next/theme/DocTagDocListPage/index.js +38 -31
  39. package/lib-next/theme/DocTagsListPage/index.js +21 -16
  40. package/lib-next/theme/Footer/Copyright/index.js +18 -0
  41. package/lib-next/theme/Footer/Layout/index.js +26 -0
  42. package/lib-next/theme/Footer/LinkItem/index.js +33 -0
  43. package/lib-next/theme/Footer/Links/MultiColumn/index.js +47 -0
  44. package/lib-next/theme/Footer/Links/Simple/index.js +41 -0
  45. package/lib-next/theme/Footer/Links/index.js +17 -0
  46. package/lib-next/theme/Footer/Logo/index.js +38 -0
  47. package/lib-next/theme/Footer/{styles.module.css → Logo/styles.module.css} +0 -0
  48. package/lib-next/theme/Footer/index.js +11 -163
  49. package/lib-next/theme/Heading/index.js +6 -4
  50. package/lib-next/theme/IconExternalLink/styles.module.css +0 -2
  51. package/lib-next/theme/Layout/index.js +15 -10
  52. package/lib-next/theme/LayoutProviders/index.js +5 -4
  53. package/lib-next/theme/MDXComponents/A.js +11 -0
  54. package/lib-next/theme/MDXComponents/Code.js +28 -0
  55. package/lib-next/theme/MDXComponents/Details.js +20 -0
  56. package/lib-next/theme/MDXComponents/Head.js +26 -0
  57. package/lib-next/theme/MDXComponents/Heading.js +11 -0
  58. package/lib-next/theme/MDXComponents/Img.js +24 -0
  59. package/lib-next/theme/MDXComponents/Img.module.css +10 -0
  60. package/lib-next/theme/MDXComponents/Pre.js +18 -0
  61. package/lib-next/theme/MDXComponents/Ul.js +23 -0
  62. package/{src/theme/MDXComponents/styles.css → lib-next/theme/MDXComponents/Ul.module.css} +1 -5
  63. package/lib-next/theme/MDXComponents/index.js +22 -71
  64. package/lib-next/theme/MDXContent/index.js +12 -0
  65. package/lib-next/theme/MDXPage/index.js +33 -28
  66. package/lib-next/theme/Navbar/ColorModeToggle/index.js +25 -0
  67. package/lib-next/theme/Navbar/Content/index.js +70 -0
  68. package/lib-next/theme/Navbar/Content/styles.module.css +15 -0
  69. package/lib-next/theme/Navbar/Layout/index.js +54 -0
  70. package/{src/theme/Navbar → lib-next/theme/Navbar/Layout}/styles.module.css +0 -13
  71. package/lib-next/theme/Navbar/Logo/index.js +17 -0
  72. package/lib-next/theme/Navbar/MobileSidebar/Header/index.js +33 -0
  73. package/lib-next/theme/Navbar/MobileSidebar/Layout/index.js +28 -0
  74. package/lib-next/theme/Navbar/MobileSidebar/PrimaryMenu/index.js +33 -0
  75. package/lib-next/theme/Navbar/MobileSidebar/SecondaryMenu/index.js +36 -0
  76. package/lib-next/theme/Navbar/MobileSidebar/Toggle/index.js +23 -0
  77. package/lib-next/theme/Navbar/MobileSidebar/index.js +31 -0
  78. package/lib-next/theme/Navbar/index.js +6 -271
  79. package/lib-next/theme/NavbarItem/DefaultNavbarItem.js +2 -6
  80. package/lib-next/theme/NavbarItem/DropdownNavbarItem.js +10 -2
  81. package/lib-next/theme/NavbarItem/NavbarNavLink.js +8 -12
  82. package/lib-next/theme/NotFound.js +38 -33
  83. package/lib-next/theme/{LayoutHead → SiteMetadata}/index.js +11 -26
  84. package/lib-next/theme/TOCCollapsible/index.js +1 -3
  85. package/lib-next/theme/Tabs/__tests__/index.test.js +5 -5
  86. package/lib-next/theme/Tabs/index.js +2 -2
  87. package/lib-next/theme/Tag/index.js +4 -4
  88. package/lib-next/theme/ThemedImage/index.js +3 -3
  89. package/package.json +17 -17
  90. package/src/getSwizzleConfig.ts +122 -1
  91. package/src/index.ts +10 -12
  92. package/src/theme/BackToTopButton/index.tsx +1 -3
  93. package/src/theme/BlogArchivePage/index.tsx +18 -17
  94. package/src/theme/BlogLayout/index.tsx +1 -1
  95. package/src/theme/BlogListPage/index.tsx +32 -13
  96. package/src/theme/BlogPostItem/index.tsx +6 -6
  97. package/src/theme/BlogPostPage/index.tsx +55 -48
  98. package/src/theme/BlogTagsListPage/index.tsx +16 -12
  99. package/src/theme/BlogTagsPostsPage/index.tsx +39 -32
  100. package/src/theme/CodeBlock/CopyButton/index.tsx +66 -0
  101. package/src/theme/CodeBlock/CopyButton/styles.module.css +66 -0
  102. package/src/theme/CodeBlock/index.tsx +6 -38
  103. package/src/theme/CodeBlock/styles.module.css +2 -22
  104. package/src/theme/ColorModeToggle/index.tsx +36 -61
  105. package/src/theme/ColorModeToggle/styles.module.css +8 -24
  106. package/src/theme/DocBreadcrumbs/index.tsx +29 -10
  107. package/src/theme/DocBreadcrumbs/styles.module.css +2 -17
  108. package/src/theme/DocCard/index.tsx +24 -17
  109. package/src/theme/DocCard/styles.module.css +8 -17
  110. package/src/theme/DocCardList/index.tsx +14 -3
  111. package/src/theme/DocCategoryGeneratedIndexPage/index.tsx +31 -4
  112. package/src/theme/DocItem/index.tsx +71 -55
  113. package/src/theme/DocPage/index.tsx +89 -90
  114. package/src/theme/DocPage/styles.module.css +0 -4
  115. package/src/theme/DocSidebar/Desktop/CollapseButton/index.tsx +38 -0
  116. package/src/theme/DocSidebar/Desktop/CollapseButton/styles.module.css +44 -0
  117. package/src/theme/DocSidebar/Desktop/Content/index.tsx +55 -0
  118. package/src/theme/DocSidebar/Desktop/Content/styles.module.css +17 -0
  119. package/src/theme/DocSidebar/Desktop/index.tsx +38 -0
  120. package/src/theme/DocSidebar/Desktop/styles.module.css +50 -0
  121. package/src/theme/DocSidebar/Mobile/index.tsx +54 -0
  122. package/src/theme/DocSidebar/index.tsx +6 -121
  123. package/src/theme/DocSidebarItem/index.tsx +15 -16
  124. package/src/theme/DocSidebarItem/styles.module.css +4 -0
  125. package/src/theme/DocTagDocListPage/index.tsx +38 -30
  126. package/src/theme/DocTagsListPage/index.tsx +21 -16
  127. package/src/theme/DocVersionBanner/index.tsx +3 -4
  128. package/src/theme/Footer/Copyright/index.tsx +20 -0
  129. package/src/theme/Footer/Layout/index.tsx +34 -0
  130. package/src/theme/Footer/LinkItem/index.tsx +36 -0
  131. package/src/theme/Footer/Links/MultiColumn/index.tsx +51 -0
  132. package/src/theme/Footer/Links/Simple/index.tsx +42 -0
  133. package/src/theme/Footer/Links/index.tsx +21 -0
  134. package/src/theme/Footer/Logo/index.tsx +41 -0
  135. package/src/theme/Footer/{styles.module.css → Logo/styles.module.css} +0 -0
  136. package/src/theme/Footer/index.tsx +12 -186
  137. package/src/theme/Heading/index.tsx +6 -4
  138. package/src/theme/IconExternalLink/styles.module.css +0 -2
  139. package/src/theme/Layout/index.tsx +15 -10
  140. package/src/theme/LayoutProviders/index.tsx +5 -4
  141. package/src/theme/MDXComponents/A.tsx +14 -0
  142. package/src/theme/MDXComponents/Code.tsx +37 -0
  143. package/src/theme/MDXComponents/Details.tsx +26 -0
  144. package/src/theme/MDXComponents/Head.tsx +29 -0
  145. package/src/theme/MDXComponents/Heading.tsx +14 -0
  146. package/src/theme/MDXComponents/Img.module.css +10 -0
  147. package/src/theme/MDXComponents/Img.tsx +26 -0
  148. package/src/theme/MDXComponents/Pre.tsx +22 -0
  149. package/{lib-next/theme/MDXComponents/styles.css → src/theme/MDXComponents/Ul.module.css} +1 -5
  150. package/src/theme/MDXComponents/Ul.tsx +28 -0
  151. package/src/theme/MDXComponents/index.tsx +23 -80
  152. package/src/theme/MDXContent/index.tsx +15 -0
  153. package/src/theme/MDXPage/index.tsx +33 -28
  154. package/src/theme/Navbar/ColorModeToggle/index.tsx +30 -0
  155. package/src/theme/Navbar/Content/index.tsx +81 -0
  156. package/src/theme/Navbar/Content/styles.module.css +15 -0
  157. package/src/theme/Navbar/Layout/index.tsx +57 -0
  158. package/{lib-next/theme/Navbar → src/theme/Navbar/Layout}/styles.module.css +0 -13
  159. package/src/theme/Navbar/Logo/index.tsx +19 -0
  160. package/src/theme/Navbar/MobileSidebar/Header/index.tsx +34 -0
  161. package/src/theme/Navbar/MobileSidebar/Layout/index.tsx +31 -0
  162. package/src/theme/Navbar/MobileSidebar/PrimaryMenu/index.tsx +38 -0
  163. package/src/theme/Navbar/MobileSidebar/SecondaryMenu/index.tsx +38 -0
  164. package/src/theme/Navbar/MobileSidebar/Toggle/index.tsx +25 -0
  165. package/src/theme/Navbar/MobileSidebar/index.tsx +33 -0
  166. package/src/theme/Navbar/index.tsx +6 -286
  167. package/src/theme/NavbarItem/DefaultNavbarItem.tsx +2 -2
  168. package/src/theme/NavbarItem/DropdownNavbarItem.tsx +11 -2
  169. package/src/theme/NavbarItem/LocaleDropdownNavbarItem/index.tsx +1 -1
  170. package/src/theme/NavbarItem/NavbarNavLink.tsx +3 -7
  171. package/src/theme/NavbarItem/index.tsx +3 -4
  172. package/src/theme/NotFound.tsx +38 -33
  173. package/src/theme/{LayoutHead → SiteMetadata}/index.tsx +11 -25
  174. package/src/theme/TOCCollapsible/index.tsx +1 -3
  175. package/src/theme/Tabs/index.tsx +5 -6
  176. package/src/theme/Tag/index.tsx +4 -4
  177. package/src/theme/ThemedImage/index.tsx +4 -3
  178. package/src/theme-classic.d.ts +286 -60
  179. package/src/translations.ts +12 -6
  180. package/src/validateThemeConfig.ts +6 -6
  181. package/lib-next/theme/DocSidebar/styles.module.css +0 -107
  182. package/lib-next/theme/Seo/index.js +0 -40
  183. package/src/theme/DocSidebar/styles.module.css +0 -107
  184. package/src/theme/Seo/index.tsx +0 -49
@@ -6,40 +6,63 @@
6
6
  */
7
7
 
8
8
  import React from 'react';
9
- import Seo from '@theme/Seo';
10
9
  import BlogLayout from '@theme/BlogLayout';
11
10
  import BlogPostItem from '@theme/BlogPostItem';
12
11
  import BlogPostPaginator from '@theme/BlogPostPaginator';
13
12
  import type {Props} from '@theme/BlogPostPage';
14
- import {ThemeClassNames} from '@docusaurus/theme-common';
13
+ import {
14
+ PageMetadata,
15
+ HtmlClassNameProvider,
16
+ ThemeClassNames,
17
+ } from '@docusaurus/theme-common';
15
18
  import TOC from '@theme/TOC';
19
+ import clsx from 'clsx';
16
20
 
17
- export default function BlogPostPage(props: Props): JSX.Element {
21
+ function BlogPostPageMetadata(props: Props): JSX.Element {
22
+ const {content: BlogPostContents} = props;
23
+ const {assets, metadata} = BlogPostContents;
24
+ const {title, description, date, tags, authors, frontMatter} = metadata;
25
+ const {keywords} = frontMatter;
26
+ const image = assets.image ?? frontMatter.image;
27
+ return (
28
+ <PageMetadata
29
+ title={title}
30
+ description={description}
31
+ keywords={keywords}
32
+ image={image}>
33
+ <meta property="og:type" content="article" />
34
+ <meta property="article:published_time" content={date} />
35
+ {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
36
+ {authors.some((author) => author.url) && (
37
+ <meta
38
+ property="article:author"
39
+ content={authors
40
+ .map((author) => author.url)
41
+ .filter(Boolean)
42
+ .join(',')}
43
+ />
44
+ )}
45
+ {tags.length > 0 && (
46
+ <meta
47
+ property="article:tag"
48
+ content={tags.map((tag) => tag.label).join(',')}
49
+ />
50
+ )}
51
+ </PageMetadata>
52
+ );
53
+ }
54
+
55
+ function BlogPostPageContent(props: Props): JSX.Element {
18
56
  const {content: BlogPostContents, sidebar} = props;
19
57
  const {assets, metadata} = BlogPostContents;
20
- const {
21
- title,
22
- description,
23
- nextItem,
24
- prevItem,
25
- date,
26
- tags,
27
- authors,
28
- frontMatter,
29
- } = metadata;
58
+ const {nextItem, prevItem, frontMatter} = metadata;
30
59
  const {
31
60
  hide_table_of_contents: hideTableOfContents,
32
- keywords,
33
61
  toc_min_heading_level: tocMinHeadingLevel,
34
62
  toc_max_heading_level: tocMaxHeadingLevel,
35
63
  } = frontMatter;
36
-
37
- const image = assets.image ?? frontMatter.image;
38
-
39
64
  return (
40
65
  <BlogLayout
41
- wrapperClassName={ThemeClassNames.wrapper.blogPages}
42
- pageClassName={ThemeClassNames.page.blogPostPage}
43
66
  sidebar={sidebar}
44
67
  toc={
45
68
  !hideTableOfContents &&
@@ -52,35 +75,6 @@ export default function BlogPostPage(props: Props): JSX.Element {
52
75
  />
53
76
  ) : undefined
54
77
  }>
55
- <Seo
56
- // TODO refactor needed: it's a bit annoying but Seo MUST be inside
57
- // BlogLayout, otherwise default image (set by BlogLayout) would shadow
58
- // the custom blog post image
59
- title={title}
60
- description={description}
61
- keywords={keywords}
62
- image={image}>
63
- <meta property="og:type" content="article" />
64
- <meta property="article:published_time" content={date} />
65
-
66
- {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
67
- {authors.some((author) => author.url) && (
68
- <meta
69
- property="article:author"
70
- content={authors
71
- .map((author) => author.url)
72
- .filter(Boolean)
73
- .join(',')}
74
- />
75
- )}
76
- {tags.length > 0 && (
77
- <meta
78
- property="article:tag"
79
- content={tags.map((tag) => tag.label).join(',')}
80
- />
81
- )}
82
- </Seo>
83
-
84
78
  <BlogPostItem
85
79
  frontMatter={frontMatter}
86
80
  assets={assets}
@@ -95,3 +89,16 @@ export default function BlogPostPage(props: Props): JSX.Element {
95
89
  </BlogLayout>
96
90
  );
97
91
  }
92
+
93
+ export default function BlogPostPage(props: Props): JSX.Element {
94
+ return (
95
+ <HtmlClassNameProvider
96
+ className={clsx(
97
+ ThemeClassNames.wrapper.blogPages,
98
+ ThemeClassNames.page.blogPostPage,
99
+ )}>
100
+ <BlogPostPageMetadata {...props} />
101
+ <BlogPostPageContent {...props} />
102
+ </HtmlClassNameProvider>
103
+ );
104
+ }
@@ -11,25 +11,29 @@ import BlogLayout from '@theme/BlogLayout';
11
11
  import TagsListByLetter from '@theme/TagsListByLetter';
12
12
  import type {Props} from '@theme/BlogTagsListPage';
13
13
  import {
14
+ PageMetadata,
15
+ HtmlClassNameProvider,
14
16
  ThemeClassNames,
15
17
  translateTagsPageTitle,
16
18
  } from '@docusaurus/theme-common';
19
+ import SearchMetadata from '../SearchMetadata';
20
+ import clsx from 'clsx';
17
21
 
18
22
  export default function BlogTagsListPage(props: Props): JSX.Element {
19
23
  const {tags, sidebar} = props;
20
24
  const title = translateTagsPageTitle();
21
25
  return (
22
- <BlogLayout
23
- title={title}
24
- wrapperClassName={ThemeClassNames.wrapper.blogPages}
25
- pageClassName={ThemeClassNames.page.blogTagsListPage}
26
- searchMetadata={{
27
- // assign unique search tag to exclude this page from search results!
28
- tag: 'blog_tags_list',
29
- }}
30
- sidebar={sidebar}>
31
- <h1>{title}</h1>
32
- <TagsListByLetter tags={Object.values(tags)} />
33
- </BlogLayout>
26
+ <HtmlClassNameProvider
27
+ className={clsx(
28
+ ThemeClassNames.wrapper.blogPages,
29
+ ThemeClassNames.page.blogTagsListPage,
30
+ )}>
31
+ <PageMetadata title={title} />
32
+ <SearchMetadata tag="blog_tags_list" />
33
+ <BlogLayout sidebar={sidebar}>
34
+ <h1>{title}</h1>
35
+ <TagsListByLetter tags={Object.values(tags)} />
36
+ </BlogLayout>
37
+ </HtmlClassNameProvider>
34
38
  );
35
39
  }
@@ -12,8 +12,15 @@ import BlogLayout from '@theme/BlogLayout';
12
12
  import BlogPostItem from '@theme/BlogPostItem';
13
13
  import type {Props} from '@theme/BlogTagsPostsPage';
14
14
  import Translate, {translate} from '@docusaurus/Translate';
15
- import {ThemeClassNames, usePluralForm} from '@docusaurus/theme-common';
15
+ import {
16
+ PageMetadata,
17
+ HtmlClassNameProvider,
18
+ ThemeClassNames,
19
+ usePluralForm,
20
+ } from '@docusaurus/theme-common';
16
21
  import BlogListPaginator from '@theme/BlogListPaginator';
22
+ import SearchMetadata from '@theme/SearchMetadata';
23
+ import clsx from 'clsx';
17
24
 
18
25
  // Very simple pluralization: probably good enough for now
19
26
  function useBlogPostsPlural() {
@@ -47,38 +54,38 @@ export default function BlogTagsPostsPage(props: Props): JSX.Element {
47
54
  );
48
55
 
49
56
  return (
50
- <BlogLayout
51
- title={title}
52
- wrapperClassName={ThemeClassNames.wrapper.blogPages}
53
- pageClassName={ThemeClassNames.page.blogTagPostListPage}
54
- searchMetadata={{
55
- // assign unique search tag to exclude this page from search results!
56
- tag: 'blog_tags_posts',
57
- }}
58
- sidebar={sidebar}>
59
- <header className="margin-bottom--xl">
60
- <h1>{title}</h1>
57
+ <HtmlClassNameProvider
58
+ className={clsx(
59
+ ThemeClassNames.wrapper.blogPages,
60
+ ThemeClassNames.page.blogTagPostListPage,
61
+ )}>
62
+ <PageMetadata title={title} />
63
+ <SearchMetadata tag="blog_tags_posts" />
64
+ <BlogLayout sidebar={sidebar}>
65
+ <header className="margin-bottom--xl">
66
+ <h1>{title}</h1>
61
67
 
62
- <Link href={allTagsPath}>
63
- <Translate
64
- id="theme.tags.tagsPageLink"
65
- description="The label of the link targeting the tag list page">
66
- View All Tags
67
- </Translate>
68
- </Link>
69
- </header>
68
+ <Link href={allTagsPath}>
69
+ <Translate
70
+ id="theme.tags.tagsPageLink"
71
+ description="The label of the link targeting the tag list page">
72
+ View All Tags
73
+ </Translate>
74
+ </Link>
75
+ </header>
70
76
 
71
- {items.map(({content: BlogPostContent}) => (
72
- <BlogPostItem
73
- key={BlogPostContent.metadata.permalink}
74
- frontMatter={BlogPostContent.frontMatter}
75
- assets={BlogPostContent.assets}
76
- metadata={BlogPostContent.metadata}
77
- truncated>
78
- <BlogPostContent />
79
- </BlogPostItem>
80
- ))}
81
- <BlogListPaginator metadata={listMetadata} />
82
- </BlogLayout>
77
+ {items.map(({content: BlogPostContent}) => (
78
+ <BlogPostItem
79
+ key={BlogPostContent.metadata.permalink}
80
+ frontMatter={BlogPostContent.frontMatter}
81
+ assets={BlogPostContent.assets}
82
+ metadata={BlogPostContent.metadata}
83
+ truncated>
84
+ <BlogPostContent />
85
+ </BlogPostItem>
86
+ ))}
87
+ <BlogListPaginator metadata={listMetadata} />
88
+ </BlogLayout>
89
+ </HtmlClassNameProvider>
83
90
  );
84
91
  }
@@ -0,0 +1,66 @@
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, {useCallback, useState, useRef, useEffect} from 'react';
9
+ import clsx from 'clsx';
10
+ import copy from 'copy-text-to-clipboard';
11
+ import {translate} from '@docusaurus/Translate';
12
+ import type {Props} from '@theme/CodeBlock/CopyButton';
13
+
14
+ import styles from './styles.module.css';
15
+
16
+ export default function CopyButton({code}: Props): JSX.Element {
17
+ const [isCopied, setIsCopied] = useState(false);
18
+ const copyTimeout = useRef<number | undefined>(undefined);
19
+ const handleCopyCode = useCallback(() => {
20
+ copy(code);
21
+ setIsCopied(true);
22
+ copyTimeout.current = window.setTimeout(() => {
23
+ setIsCopied(false);
24
+ }, 1000);
25
+ }, [code]);
26
+
27
+ useEffect(() => () => window.clearTimeout(copyTimeout.current), []);
28
+
29
+ return (
30
+ <button
31
+ type="button"
32
+ aria-label={
33
+ isCopied
34
+ ? translate({
35
+ id: 'theme.CodeBlock.copied',
36
+ message: 'Copied',
37
+ description: 'The copied button label on code blocks',
38
+ })
39
+ : translate({
40
+ id: 'theme.CodeBlock.copyButtonAriaLabel',
41
+ message: 'Copy code to clipboard',
42
+ description: 'The ARIA label for copy code blocks button',
43
+ })
44
+ }
45
+ title={translate({
46
+ id: 'theme.CodeBlock.copy',
47
+ message: 'Copy',
48
+ description: 'The copy button label on code blocks',
49
+ })}
50
+ className={clsx(
51
+ styles.copyButton,
52
+ 'clean-btn',
53
+ isCopied && styles.copyButtonCopied,
54
+ )}
55
+ onClick={handleCopyCode}>
56
+ <span className={styles.copyButtonIcons} aria-hidden="true">
57
+ <svg className={styles.copyButtonIcon} viewBox="0 0 24 24">
58
+ <path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" />
59
+ </svg>
60
+ <svg className={styles.copyButtonSuccessIcon} viewBox="0 0 24 24">
61
+ <path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z" />
62
+ </svg>
63
+ </span>
64
+ </button>
65
+ );
66
+ }
@@ -0,0 +1,66 @@
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
+ .copyButton {
9
+ display: flex;
10
+ background: inherit;
11
+ border: 1px solid var(--ifm-color-emphasis-300);
12
+ border-radius: var(--ifm-global-radius);
13
+ padding: 0.4rem;
14
+ position: absolute;
15
+ right: calc(var(--ifm-pre-padding) / 2);
16
+ top: calc(var(--ifm-pre-padding) / 2);
17
+ transition: opacity 200ms ease-in-out;
18
+ opacity: 0;
19
+ }
20
+
21
+ .copyButton:focus-visible,
22
+ .copyButton:hover,
23
+ :global(.theme-code-block:hover) .copyButtonCopied {
24
+ opacity: 1 !important;
25
+ }
26
+
27
+ :global(.theme-code-block:hover) .copyButton:not(.copyButtonCopied) {
28
+ opacity: 0.4;
29
+ }
30
+
31
+ .copyButtonIcons {
32
+ position: relative;
33
+ width: 1.125rem;
34
+ height: 1.125rem;
35
+ }
36
+
37
+ .copyButtonIcon,
38
+ .copyButtonSuccessIcon {
39
+ position: absolute;
40
+ top: 0;
41
+ left: 0;
42
+ fill: currentColor;
43
+ opacity: inherit;
44
+ width: inherit;
45
+ height: inherit;
46
+ transition: all 0.15s ease;
47
+ }
48
+
49
+ .copyButtonSuccessIcon {
50
+ top: 50%;
51
+ left: 50%;
52
+ transform: translate(-50%, -50%) scale(0.33);
53
+ opacity: 0;
54
+ color: #00d600;
55
+ }
56
+
57
+ .copyButtonCopied .copyButtonIcon {
58
+ transform: scale(0.33);
59
+ opacity: 0;
60
+ }
61
+
62
+ .copyButtonCopied .copyButtonSuccessIcon {
63
+ transform: translate(-50%, -50%) scale(1);
64
+ opacity: 1;
65
+ transition-delay: 0.075s;
66
+ }
@@ -8,8 +8,6 @@
8
8
  import React, {isValidElement, useEffect, useState} from 'react';
9
9
  import clsx from 'clsx';
10
10
  import Highlight, {defaultProps, type Language} from 'prism-react-renderer';
11
- import copy from 'copy-text-to-clipboard';
12
- import Translate, {translate} from '@docusaurus/Translate';
13
11
  import {
14
12
  useThemeConfig,
15
13
  parseCodeBlockTitle,
@@ -18,6 +16,7 @@ import {
18
16
  ThemeClassNames,
19
17
  usePrismTheme,
20
18
  } from '@docusaurus/theme-common';
19
+ import CopyButton from '@theme/CodeBlock/CopyButton';
21
20
  import type {Props} from '@theme/CodeBlock';
22
21
 
23
22
  import styles from './styles.module.css';
@@ -31,7 +30,6 @@ export default function CodeBlock({
31
30
  }: Props): JSX.Element {
32
31
  const {prism} = useThemeConfig();
33
32
 
34
- const [showCopied, setShowCopied] = useState(false);
35
33
  const [mounted, setMounted] = useState(false);
36
34
  // The Prism theme on SSR is always the default theme but the site theme
37
35
  // can be in a different mode. React hydration doesn't update DOM styles
@@ -90,13 +88,6 @@ export default function CodeBlock({
90
88
  languageProp ?? parseLanguage(blockClassName) ?? prism.defaultLanguage;
91
89
  const {highlightLines, code} = parseLines(content, metastring, language);
92
90
 
93
- const handleCopyCode = () => {
94
- copy(code);
95
- setShowCopied(true);
96
-
97
- setTimeout(() => setShowCopied(false), 2000);
98
- };
99
-
100
91
  return (
101
92
  <Highlight
102
93
  {...defaultProps}
@@ -120,16 +111,15 @@ export default function CodeBlock({
120
111
  {codeBlockTitle}
121
112
  </div>
122
113
  )}
123
- <div className={clsx(styles.codeBlockContent, language)}>
114
+ <div className={styles.codeBlockContent} style={style}>
124
115
  <pre
125
116
  /* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */
126
117
  tabIndex={0}
127
- className={clsx(className, styles.codeBlock, 'thin-scrollbar')}
128
- style={style}>
118
+ className={clsx(className, styles.codeBlock, 'thin-scrollbar')}>
129
119
  <code className={styles.codeBlockLines}>
130
120
  {tokens.map((line, i) => {
131
- if (line.length === 1 && line[0].content === '\n') {
132
- line[0].content = '';
121
+ if (line.length === 1 && line[0]!.content === '\n') {
122
+ line[0]!.content = '';
133
123
  }
134
124
 
135
125
  const lineProps = getLineProps({line, key: i});
@@ -150,29 +140,7 @@ export default function CodeBlock({
150
140
  </code>
151
141
  </pre>
152
142
 
153
- <button
154
- type="button"
155
- aria-label={translate({
156
- id: 'theme.CodeBlock.copyButtonAriaLabel',
157
- message: 'Copy code to clipboard',
158
- description: 'The ARIA label for copy code blocks button',
159
- })}
160
- className={clsx(styles.copyButton, 'clean-btn')}
161
- onClick={handleCopyCode}>
162
- {showCopied ? (
163
- <Translate
164
- id="theme.CodeBlock.copied"
165
- description="The copied button label on code blocks">
166
- Copied
167
- </Translate>
168
- ) : (
169
- <Translate
170
- id="theme.CodeBlock.copy"
171
- description="The copy button label on code blocks">
172
- Copy
173
- </Translate>
174
- )}
175
- </button>
143
+ <CopyButton code={code} />
176
144
  </div>
177
145
  </div>
178
146
  )}
@@ -7,7 +7,6 @@
7
7
 
8
8
  .codeBlockContainer {
9
9
  margin-bottom: var(--ifm-leading);
10
- border-radius: var(--ifm-global-radius);
11
10
  box-shadow: var(--ifm-global-shadow-lw);
12
11
  }
13
12
 
@@ -15,6 +14,7 @@
15
14
  position: relative;
16
15
  /* rtl:ignore */
17
16
  direction: ltr;
17
+ border-radius: var(--ifm-global-radius);
18
18
  }
19
19
 
20
20
  .codeBlockTitle {
@@ -29,7 +29,7 @@
29
29
  .codeBlock {
30
30
  margin: 0;
31
31
  padding: 0;
32
- border-radius: var(--ifm-global-radius);
32
+ background-color: inherit;
33
33
  }
34
34
 
35
35
  .codeBlockTitle + .codeBlockContent .codeBlock {
@@ -39,26 +39,6 @@
39
39
 
40
40
  .codeBlockStandalone {
41
41
  padding: 0;
42
- border-radius: var(--ifm-global-radius);
43
- }
44
-
45
- .copyButton {
46
- background: rgb(0 0 0 / 30%);
47
- border-radius: var(--ifm-global-radius);
48
- color: var(--ifm-color-white);
49
- opacity: 0;
50
- user-select: none;
51
- padding: 0.4rem 0.5rem;
52
- position: absolute;
53
- right: calc(var(--ifm-pre-padding) / 2);
54
- top: calc(var(--ifm-pre-padding) / 2);
55
- transition: opacity 200ms ease-in-out;
56
- }
57
-
58
- .copyButton:focus,
59
- .codeBlockContent:hover > .copyButton,
60
- .codeBlockTitle:hover + .codeBlockContent .copyButton {
61
- opacity: 1;
62
42
  }
63
43
 
64
44
  .codeBlockLines {
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import React, {useState, useRef, useEffect} from 'react';
8
+ import React from 'react';
9
9
  import type {Props} from '@theme/ColorModeToggle';
10
10
  import useIsBrowser from '@docusaurus/useIsBrowser';
11
11
  import {translate} from '@docusaurus/Translate';
@@ -15,76 +15,51 @@ import IconDarkMode from '@theme/IconDarkMode';
15
15
  import clsx from 'clsx';
16
16
  import styles from './styles.module.css';
17
17
 
18
- function ColorModeToggle({
19
- className,
20
- checked: defaultChecked,
21
- onChange,
22
- }: Props): JSX.Element {
18
+ function ColorModeToggle({className, value, onChange}: Props): JSX.Element {
23
19
  const isBrowser = useIsBrowser();
24
- const [checked, setChecked] = useState(defaultChecked);
25
- const [focused, setFocused] = useState(false);
26
- const inputRef = useRef<HTMLInputElement>(null);
27
20
 
28
- useEffect(() => {
29
- setChecked(defaultChecked);
30
- }, [defaultChecked]);
21
+ const title = translate(
22
+ {
23
+ message: 'Switch between dark and light mode (currently {mode})',
24
+ id: 'theme.colorToggle.ariaLabel',
25
+ description: 'The ARIA label for the navbar color mode toggle',
26
+ },
27
+ {
28
+ mode:
29
+ value === 'dark'
30
+ ? translate({
31
+ message: 'dark mode',
32
+ id: 'theme.colorToggle.ariaLabel.mode.dark',
33
+ description: 'The name for the dark color mode',
34
+ })
35
+ : translate({
36
+ message: 'light mode',
37
+ id: 'theme.colorToggle.ariaLabel.mode.light',
38
+ description: 'The name for the light color mode',
39
+ }),
40
+ },
41
+ );
31
42
 
32
43
  return (
33
- <div
34
- className={clsx(styles.toggle, className, {
35
- [styles.toggleChecked]: checked,
36
- [styles.toggleFocused]: focused,
37
- [styles.toggleDisabled]: !isBrowser,
38
- })}>
39
- {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
40
- <div
41
- className={styles.toggleButton}
42
- role="button"
43
- tabIndex={-1}
44
- onClick={() => inputRef.current?.click()}>
44
+ <div className={clsx(styles.toggle, className)}>
45
+ <button
46
+ className={clsx(
47
+ 'clean-btn',
48
+ styles.toggleButton,
49
+ !isBrowser && styles.toggleButtonDisabled,
50
+ )}
51
+ type="button"
52
+ onClick={() => onChange(value === 'dark' ? 'light' : 'dark')}
53
+ disabled={!isBrowser}
54
+ title={title}
55
+ aria-label={title}>
45
56
  <IconLightMode
46
57
  className={clsx(styles.toggleIcon, styles.lightToggleIcon)}
47
58
  />
48
59
  <IconDarkMode
49
60
  className={clsx(styles.toggleIcon, styles.darkToggleIcon)}
50
61
  />
51
- </div>
52
-
53
- <input
54
- ref={inputRef}
55
- checked={checked}
56
- type="checkbox"
57
- className={styles.toggleScreenReader}
58
- aria-label={translate(
59
- {
60
- message: 'Switch between dark and light mode (currently {mode})',
61
- id: 'theme.colorToggle.ariaLabel',
62
- description: 'The ARIA label for the navbar color mode toggle',
63
- },
64
- {
65
- mode: checked
66
- ? translate({
67
- message: 'dark mode',
68
- id: 'theme.colorToggle.ariaLabel.mode.dark',
69
- description: 'The name for the dark color mode',
70
- })
71
- : translate({
72
- message: 'light mode',
73
- id: 'theme.colorToggle.ariaLabel.mode.light',
74
- description: 'The name for the light color mode',
75
- }),
76
- },
77
- )}
78
- onChange={onChange}
79
- onClick={() => setChecked(!checked)}
80
- onFocus={() => setFocused(true)}
81
- onBlur={() => setFocused(false)}
82
- onKeyDown={(e) => {
83
- if (e.key === 'Enter') {
84
- inputRef.current?.click();
85
- }
86
- }}
87
- />
62
+ </button>
88
63
  </div>
89
64
  );
90
65
  }