@docusaurus/theme-classic 3.7.0 → 3.8.0-canary-6324

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 (136) hide show
  1. package/lib/getSwizzleConfig.js +1 -1
  2. package/lib/index.js +9 -0
  3. package/lib/inlineScripts.js +6 -27
  4. package/lib/inlineSvgSprites.d.ts +7 -0
  5. package/lib/inlineSvgSprites.js +14 -0
  6. package/lib/options.js +5 -0
  7. package/lib/theme/AnnouncementBar/index.js +6 -2
  8. package/lib/theme/CodeBlock/{WordWrapButton → Buttons/Button}/index.d.ts +2 -2
  9. package/lib/theme/CodeBlock/Buttons/Button/index.js +13 -0
  10. package/lib/theme/CodeBlock/Buttons/CopyButton/index.d.ts +9 -0
  11. package/lib/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/index.js +36 -25
  12. package/lib/theme/CodeBlock/Buttons/WordWrapButton/index.d.ts +9 -0
  13. package/lib/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/index.js +12 -7
  14. package/lib/theme/CodeBlock/{CopyButton → Buttons}/index.d.ts +2 -2
  15. package/lib/theme/CodeBlock/Buttons/index.js +27 -0
  16. package/lib/theme/CodeBlock/Buttons/styles.module.css +37 -0
  17. package/lib/theme/CodeBlock/Content/Element.js +4 -2
  18. package/lib/theme/CodeBlock/Content/String.d.ts +1 -1
  19. package/lib/theme/CodeBlock/Content/String.js +22 -92
  20. package/lib/theme/CodeBlock/Content/index.d.ts +9 -0
  21. package/lib/theme/CodeBlock/Content/index.js +74 -0
  22. package/lib/theme/CodeBlock/Content/styles.module.css +0 -52
  23. package/lib/theme/CodeBlock/Layout/index.d.ts +9 -0
  24. package/lib/theme/CodeBlock/Layout/index.js +30 -0
  25. package/lib/theme/CodeBlock/Layout/styles.module.css +27 -0
  26. package/lib/theme/CodeBlock/Line/Token/index.d.ts +9 -0
  27. package/lib/theme/CodeBlock/Line/Token/index.js +11 -0
  28. package/lib/theme/CodeBlock/Line/index.d.ts +1 -1
  29. package/lib/theme/CodeBlock/Line/index.js +21 -7
  30. package/lib/theme/CodeBlock/Title/index.d.ts +9 -0
  31. package/lib/theme/CodeBlock/Title/index.js +10 -0
  32. package/lib/theme/ColorModeToggle/index.d.ts +1 -1
  33. package/lib/theme/ColorModeToggle/index.js +85 -27
  34. package/lib/theme/ColorModeToggle/styles.module.css +7 -2
  35. package/lib/theme/DocBreadcrumbs/StructuredData/index.d.ts +9 -0
  36. package/lib/theme/DocBreadcrumbs/StructuredData/index.js +21 -0
  37. package/lib/theme/DocBreadcrumbs/index.js +36 -54
  38. package/lib/theme/DocCard/index.js +6 -4
  39. package/lib/theme/DocCardList/index.js +12 -6
  40. package/lib/theme/DocCardList/styles.module.css +14 -0
  41. package/lib/theme/DocCategoryGeneratedIndexPage/index.js +1 -1
  42. package/lib/theme/DocCategoryGeneratedIndexPage/styles.module.css +0 -8
  43. package/lib/theme/DocItem/Paginator/index.js +7 -1
  44. package/lib/theme/DocPaginator/index.js +3 -2
  45. package/lib/theme/DocSidebarItem/Category/index.js +8 -1
  46. package/lib/theme/Footer/Layout/index.js +2 -1
  47. package/lib/theme/Footer/Links/MultiColumn/index.js +7 -1
  48. package/lib/theme/Icon/ExternalLink/index.js +4 -5
  49. package/lib/theme/Icon/SystemColorMode/index.d.ts +9 -0
  50. package/lib/theme/Icon/SystemColorMode/index.js +16 -0
  51. package/lib/theme/Layout/index.js +1 -0
  52. package/lib/theme/Navbar/ColorModeToggle/index.js +5 -4
  53. package/lib/theme/Navbar/Content/index.js +20 -3
  54. package/lib/theme/Navbar/Content/styles.module.css +8 -0
  55. package/lib/theme/Navbar/Layout/index.js +2 -1
  56. package/lib/theme/Navbar/MobileSidebar/Layout/index.js +35 -4
  57. package/lib/theme/NavbarItem/DefaultNavbarItem/Desktop/index.d.ts +9 -0
  58. package/lib/theme/NavbarItem/DefaultNavbarItem/Desktop/index.js +29 -0
  59. package/lib/theme/NavbarItem/DefaultNavbarItem/Mobile/index.d.ts +9 -0
  60. package/lib/theme/NavbarItem/DefaultNavbarItem/Mobile/index.js +20 -0
  61. package/lib/theme/NavbarItem/DefaultNavbarItem/index.js +25 -0
  62. package/lib/theme/NavbarItem/DocsVersionDropdownNavbarItem.d.ts +1 -1
  63. package/lib/theme/NavbarItem/DocsVersionDropdownNavbarItem.js +50 -7
  64. package/lib/theme/NavbarItem/DropdownNavbarItem/Desktop/index.d.ts +9 -0
  65. package/lib/theme/NavbarItem/DropdownNavbarItem/Desktop/index.js +74 -0
  66. package/lib/theme/NavbarItem/DropdownNavbarItem/Mobile/index.d.ts +10 -0
  67. package/lib/theme/NavbarItem/DropdownNavbarItem/Mobile/index.js +142 -0
  68. package/lib/theme/NavbarItem/DropdownNavbarItem/index.js +3 -142
  69. package/lib/theme/Tag/index.js +1 -0
  70. package/lib/theme/ThemeProvider/TitleFormatter/index.d.ts +9 -0
  71. package/lib/theme/ThemeProvider/TitleFormatter/index.js +19 -0
  72. package/lib/theme/ThemeProvider/index.d.ts +9 -0
  73. package/lib/theme/ThemeProvider/index.js +11 -0
  74. package/package.json +15 -15
  75. package/src/getSwizzleConfig.ts +1 -1
  76. package/src/index.ts +9 -0
  77. package/src/inlineScripts.ts +6 -27
  78. package/src/inlineSvgSprites.ts +12 -0
  79. package/src/options.ts +15 -0
  80. package/src/theme/AnnouncementBar/index.tsx +6 -2
  81. package/src/theme/CodeBlock/Buttons/Button/index.tsx +19 -0
  82. package/src/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/index.tsx +42 -26
  83. package/src/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/index.tsx +15 -12
  84. package/src/theme/CodeBlock/Buttons/index.tsx +32 -0
  85. package/src/theme/CodeBlock/Buttons/styles.module.css +37 -0
  86. package/src/theme/CodeBlock/Content/Element.tsx +4 -2
  87. package/src/theme/CodeBlock/Content/String.tsx +24 -101
  88. package/src/theme/CodeBlock/Content/index.tsx +84 -0
  89. package/src/theme/CodeBlock/Content/styles.module.css +0 -52
  90. package/src/theme/CodeBlock/Layout/index.tsx +34 -0
  91. package/src/theme/CodeBlock/Layout/styles.module.css +27 -0
  92. package/src/theme/CodeBlock/Line/Token/index.tsx +18 -0
  93. package/src/theme/CodeBlock/Line/index.tsx +26 -7
  94. package/src/theme/CodeBlock/Title/index.tsx +15 -0
  95. package/src/theme/ColorModeToggle/index.tsx +102 -32
  96. package/src/theme/ColorModeToggle/styles.module.css +7 -2
  97. package/src/theme/DocBreadcrumbs/StructuredData/index.tsx +24 -0
  98. package/src/theme/DocBreadcrumbs/index.tsx +35 -57
  99. package/src/theme/DocCard/index.tsx +8 -2
  100. package/src/theme/DocCardList/index.tsx +18 -7
  101. package/src/theme/DocCardList/styles.module.css +14 -0
  102. package/src/theme/DocCategoryGeneratedIndexPage/index.tsx +1 -1
  103. package/src/theme/DocCategoryGeneratedIndexPage/styles.module.css +0 -8
  104. package/src/theme/DocItem/Paginator/index.tsx +7 -1
  105. package/src/theme/DocPaginator/index.tsx +3 -2
  106. package/src/theme/DocSidebarItem/Category/index.tsx +8 -1
  107. package/src/theme/Footer/Layout/index.tsx +2 -1
  108. package/src/theme/Footer/Links/MultiColumn/index.tsx +7 -1
  109. package/src/theme/Icon/ExternalLink/index.tsx +5 -5
  110. package/src/theme/Icon/SystemColorMode/index.tsx +20 -0
  111. package/src/theme/Layout/index.tsx +1 -0
  112. package/src/theme/Navbar/ColorModeToggle/index.tsx +5 -4
  113. package/src/theme/Navbar/Content/index.tsx +20 -3
  114. package/src/theme/Navbar/Content/styles.module.css +8 -0
  115. package/src/theme/Navbar/Layout/index.tsx +2 -1
  116. package/src/theme/Navbar/MobileSidebar/Layout/index.tsx +43 -4
  117. package/src/theme/NavbarItem/DefaultNavbarItem/Desktop/index.tsx +34 -0
  118. package/src/theme/NavbarItem/DefaultNavbarItem/Mobile/index.tsx +23 -0
  119. package/src/theme/NavbarItem/DefaultNavbarItem/index.tsx +28 -0
  120. package/src/theme/NavbarItem/DocsVersionDropdownNavbarItem.tsx +87 -9
  121. package/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx +86 -0
  122. package/src/theme/NavbarItem/DropdownNavbarItem/Mobile/index.tsx +166 -0
  123. package/src/theme/NavbarItem/DropdownNavbarItem/index.tsx +4 -168
  124. package/src/theme/Tag/index.tsx +1 -0
  125. package/src/theme/ThemeProvider/TitleFormatter/index.tsx +27 -0
  126. package/src/theme/ThemeProvider/index.tsx +14 -0
  127. package/src/theme-classic.d.ts +175 -20
  128. package/lib/theme/NavbarItem/DefaultNavbarItem.js +0 -52
  129. package/src/theme/NavbarItem/DefaultNavbarItem.tsx +0 -66
  130. /package/lib/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/styles.module.css +0 -0
  131. /package/lib/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/styles.module.css +0 -0
  132. /package/lib/theme/NavbarItem/{DefaultNavbarItem.d.ts → DefaultNavbarItem/index.d.ts} +0 -0
  133. /package/lib/theme/NavbarItem/DropdownNavbarItem/{styles.module.css → Mobile/styles.module.css} +0 -0
  134. /package/src/theme/CodeBlock/{CopyButton → Buttons/CopyButton}/styles.module.css +0 -0
  135. /package/src/theme/CodeBlock/{WordWrapButton → Buttons/WordWrapButton}/styles.module.css +0 -0
  136. /package/src/theme/NavbarItem/DropdownNavbarItem/{styles.module.css → Mobile/styles.module.css} +0 -0
@@ -6,7 +6,12 @@
6
6
  */
7
7
 
8
8
  import React, {type ReactNode} from 'react';
9
- import {useThemeConfig, ErrorCauseBoundary} from '@docusaurus/theme-common';
9
+ import clsx from 'clsx';
10
+ import {
11
+ useThemeConfig,
12
+ ErrorCauseBoundary,
13
+ ThemeClassNames,
14
+ } from '@docusaurus/theme-common';
10
15
  import {
11
16
  splitNavbarItems,
12
17
  useNavbarMobileSidebar,
@@ -55,8 +60,20 @@ function NavbarContentLayout({
55
60
  }) {
56
61
  return (
57
62
  <div className="navbar__inner">
58
- <div className="navbar__items">{left}</div>
59
- <div className="navbar__items navbar__items--right">{right}</div>
63
+ <div
64
+ className={clsx(
65
+ ThemeClassNames.layout.navbar.containerLeft,
66
+ 'navbar__items',
67
+ )}>
68
+ {left}
69
+ </div>
70
+ <div
71
+ className={clsx(
72
+ ThemeClassNames.layout.navbar.containerRight,
73
+ 'navbar__items navbar__items--right',
74
+ )}>
75
+ {right}
76
+ </div>
60
77
  </div>
61
78
  );
62
79
  }
@@ -13,3 +13,11 @@ Hide color mode toggle in small viewports
13
13
  display: none;
14
14
  }
15
15
  }
16
+
17
+ /*
18
+ Restore some Infima style that broke with CSS Cascade Layers
19
+ See https://github.com/facebook/docusaurus/pull/11142
20
+ */
21
+ :global(.navbar__items--right) > :last-child {
22
+ padding-right: 0;
23
+ }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import React, {type ComponentProps, type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
- import {useThemeConfig} from '@docusaurus/theme-common';
10
+ import {ThemeClassNames, useThemeConfig} from '@docusaurus/theme-common';
11
11
  import {
12
12
  useHideableNavbar,
13
13
  useNavbarMobileSidebar,
@@ -43,6 +43,7 @@ export default function NavbarLayout({children}: Props): ReactNode {
43
43
  description: 'The ARIA label for the main navigation',
44
44
  })}
45
45
  className={clsx(
46
+ ThemeClassNames.layout.navbar.container,
46
47
  'navbar',
47
48
  'navbar--fixed-top',
48
49
  hideOnScroll && [
@@ -5,11 +5,42 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import React, {type ReactNode} from 'react';
8
+ import React, {version, type ReactNode} from 'react';
9
9
  import clsx from 'clsx';
10
10
  import {useNavbarSecondaryMenu} from '@docusaurus/theme-common/internal';
11
+ import {ThemeClassNames} from '@docusaurus/theme-common';
11
12
  import type {Props} from '@theme/Navbar/MobileSidebar/Layout';
12
13
 
14
+ // TODO Docusaurus v4: remove temporary inert workaround
15
+ // See https://github.com/facebook/react/issues/17157
16
+ // See https://github.com/radix-ui/themes/pull/509
17
+ function inertProps(inert: boolean) {
18
+ const isBeforeReact19 = parseInt(version!.split('.')[0]!, 10) < 19;
19
+ if (isBeforeReact19) {
20
+ return {inert: inert ? '' : undefined};
21
+ }
22
+ return {inert};
23
+ }
24
+
25
+ function NavbarMobileSidebarPanel({
26
+ children,
27
+ inert,
28
+ }: {
29
+ children: ReactNode;
30
+ inert: boolean;
31
+ }) {
32
+ return (
33
+ <div
34
+ className={clsx(
35
+ ThemeClassNames.layout.navbar.mobileSidebar.panel,
36
+ 'navbar-sidebar__item menu',
37
+ )}
38
+ {...inertProps(inert)}>
39
+ {children}
40
+ </div>
41
+ );
42
+ }
43
+
13
44
  export default function NavbarMobileSidebarLayout({
14
45
  header,
15
46
  primaryMenu,
@@ -17,14 +48,22 @@ export default function NavbarMobileSidebarLayout({
17
48
  }: Props): ReactNode {
18
49
  const {shown: secondaryMenuShown} = useNavbarSecondaryMenu();
19
50
  return (
20
- <div className="navbar-sidebar">
51
+ <div
52
+ className={clsx(
53
+ ThemeClassNames.layout.navbar.mobileSidebar.container,
54
+ 'navbar-sidebar',
55
+ )}>
21
56
  {header}
22
57
  <div
23
58
  className={clsx('navbar-sidebar__items', {
24
59
  'navbar-sidebar__items--show-secondary': secondaryMenuShown,
25
60
  })}>
26
- <div className="navbar-sidebar__item menu">{primaryMenu}</div>
27
- <div className="navbar-sidebar__item menu">{secondaryMenu}</div>
61
+ <NavbarMobileSidebarPanel inert={secondaryMenuShown}>
62
+ {primaryMenu}
63
+ </NavbarMobileSidebarPanel>
64
+ <NavbarMobileSidebarPanel inert={!secondaryMenuShown}>
65
+ {secondaryMenu}
66
+ </NavbarMobileSidebarPanel>
28
67
  </div>
29
68
  </div>
30
69
  );
@@ -0,0 +1,34 @@
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, {type ReactNode} from 'react';
9
+ import clsx from 'clsx';
10
+ import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
11
+ import type {Props} from '@theme/NavbarItem/DefaultNavbarItem/Desktop';
12
+
13
+ export default function DefaultNavbarItemDesktop({
14
+ className,
15
+ isDropdownItem = false,
16
+ ...props
17
+ }: Props): ReactNode {
18
+ const element = (
19
+ <NavbarNavLink
20
+ className={clsx(
21
+ isDropdownItem ? 'dropdown__link' : 'navbar__item navbar__link',
22
+ className,
23
+ )}
24
+ isDropdownLink={isDropdownItem}
25
+ {...props}
26
+ />
27
+ );
28
+
29
+ if (isDropdownItem) {
30
+ return <li>{element}</li>;
31
+ }
32
+
33
+ return element;
34
+ }
@@ -0,0 +1,23 @@
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, {type ReactNode} from 'react';
9
+ import clsx from 'clsx';
10
+ import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
11
+ import type {Props} from '@theme/NavbarItem/DefaultNavbarItem/Mobile';
12
+
13
+ export default function DefaultNavbarItemMobile({
14
+ className,
15
+ isDropdownItem,
16
+ ...props
17
+ }: Props): ReactNode {
18
+ return (
19
+ <li className="menu__list-item">
20
+ <NavbarNavLink className={clsx('menu__link', className)} {...props} />
21
+ </li>
22
+ );
23
+ }
@@ -0,0 +1,28 @@
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, {type ReactNode} from 'react';
9
+ import DefaultNavbarItemMobile from '@theme/NavbarItem/DefaultNavbarItem/Mobile';
10
+ import DefaultNavbarItemDesktop from '@theme/NavbarItem/DefaultNavbarItem/Desktop';
11
+ import type {Props} from '@theme/NavbarItem/DefaultNavbarItem';
12
+
13
+ export default function DefaultNavbarItem({
14
+ mobile = false,
15
+ position, // Need to destructure position from props so that it doesn't get passed on.
16
+ ...props
17
+ }: Props): ReactNode {
18
+ const Comp = mobile ? DefaultNavbarItemMobile : DefaultNavbarItemDesktop;
19
+ return (
20
+ <Comp
21
+ {...props}
22
+ activeClassName={
23
+ props.activeClassName ??
24
+ (mobile ? 'menu__link--active' : 'navbar__link--active')
25
+ }
26
+ />
27
+ );
28
+ }
@@ -16,7 +16,11 @@ import {translate} from '@docusaurus/Translate';
16
16
  import {useLocation} from '@docusaurus/router';
17
17
  import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
18
18
  import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
19
- import type {Props} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
19
+ import type {
20
+ Props,
21
+ PropVersions,
22
+ PropVersionItem,
23
+ } from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
20
24
  import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
21
25
  import type {
22
26
  GlobalVersion,
@@ -24,6 +28,56 @@ import type {
24
28
  ActiveDocContext,
25
29
  } from '@docusaurus/plugin-content-docs/client';
26
30
 
31
+ type VersionItem = {
32
+ version: GlobalVersion;
33
+ label: string;
34
+ };
35
+
36
+ function getVersionItems(
37
+ versions: GlobalVersion[],
38
+ configs?: PropVersions,
39
+ ): VersionItem[] {
40
+ if (configs) {
41
+ // Collect all the versions we have
42
+ const versionMap = new Map<string, GlobalVersion>(
43
+ versions.map((version) => [version.name, version]),
44
+ );
45
+
46
+ const toVersionItem = (
47
+ name: string,
48
+ config?: PropVersionItem,
49
+ ): VersionItem => {
50
+ const version = versionMap.get(name);
51
+ if (!version) {
52
+ throw new Error(`No docs version exist for name '${name}', please verify your 'docsVersionDropdown' navbar item versions config.
53
+ Available version names:\n- ${versions.map((v) => `${v.name}`).join('\n- ')}`);
54
+ }
55
+ return {version, label: config?.label ?? version.label};
56
+ };
57
+
58
+ if (Array.isArray(configs)) {
59
+ return configs.map((name) => toVersionItem(name, undefined));
60
+ } else {
61
+ return Object.entries(configs).map(([name, config]) =>
62
+ toVersionItem(name, config),
63
+ );
64
+ }
65
+ } else {
66
+ return versions.map((version) => ({version, label: version.label}));
67
+ }
68
+ }
69
+
70
+ function useVersionItems({
71
+ docsPluginId,
72
+ configs,
73
+ }: {
74
+ docsPluginId: Props['docsPluginId'];
75
+ configs: Props['versions'];
76
+ }): VersionItem[] {
77
+ const versions = useVersions(docsPluginId);
78
+ return getVersionItems(versions, configs);
79
+ }
80
+
27
81
  function getVersionMainDoc(version: GlobalVersion): GlobalDoc {
28
82
  return version.docs.find((doc) => doc.id === version.mainDocId)!;
29
83
  }
@@ -40,23 +94,47 @@ function getVersionTargetDoc(
40
94
  );
41
95
  }
42
96
 
97
+ // The version item to use for the "dropdown button"
98
+ function useDisplayedVersionItem({
99
+ docsPluginId,
100
+ versionItems,
101
+ }: {
102
+ docsPluginId: Props['docsPluginId'];
103
+ versionItems: VersionItem[];
104
+ }): VersionItem {
105
+ // The order of the candidates matters!
106
+ const candidates = useDocsVersionCandidates(docsPluginId);
107
+ const candidateItems = candidates
108
+ .map((candidate) => versionItems.find((vi) => vi.version === candidate))
109
+ .filter((vi) => vi !== undefined);
110
+ return candidateItems[0] ?? versionItems[0]!;
111
+ }
112
+
43
113
  export default function DocsVersionDropdownNavbarItem({
44
114
  mobile,
45
115
  docsPluginId,
46
116
  dropdownActiveClassDisabled,
47
117
  dropdownItemsBefore,
48
118
  dropdownItemsAfter,
119
+ versions: configs,
49
120
  ...props
50
121
  }: Props): ReactNode {
51
122
  const {search, hash} = useLocation();
52
123
  const activeDocContext = useActiveDocContext(docsPluginId);
53
- const versions = useVersions(docsPluginId);
54
124
  const {savePreferredVersionName} = useDocsPreferredVersion(docsPluginId);
125
+ const versionItems = useVersionItems({docsPluginId, configs});
126
+ const displayedVersionItem = useDisplayedVersionItem({
127
+ docsPluginId,
128
+ versionItems,
129
+ });
55
130
 
56
- function versionToLink(version: GlobalVersion): LinkLikeNavbarItemProps {
131
+ function versionItemToLink({
132
+ version,
133
+ label,
134
+ }: VersionItem): LinkLikeNavbarItemProps {
57
135
  const targetDoc = getVersionTargetDoc(version, activeDocContext);
58
136
  return {
59
- label: version.label,
137
+ label,
60
138
  // preserve ?search#hash suffix on version switches
61
139
  to: `${targetDoc.path}${search}${hash}`,
62
140
  isActive: () => version === activeDocContext.activeVersion,
@@ -66,12 +144,10 @@ export default function DocsVersionDropdownNavbarItem({
66
144
 
67
145
  const items: LinkLikeNavbarItemProps[] = [
68
146
  ...dropdownItemsBefore,
69
- ...versions.map(versionToLink),
147
+ ...versionItems.map(versionItemToLink),
70
148
  ...dropdownItemsAfter,
71
149
  ];
72
150
 
73
- const dropdownVersion = useDocsVersionCandidates(docsPluginId)[0];
74
-
75
151
  // Mobile dropdown is handled a bit differently
76
152
  const dropdownLabel =
77
153
  mobile && items.length > 1
@@ -81,11 +157,13 @@ export default function DocsVersionDropdownNavbarItem({
81
157
  description:
82
158
  'The label for the navbar versions dropdown on mobile view',
83
159
  })
84
- : dropdownVersion.label;
160
+ : displayedVersionItem.label;
161
+
85
162
  const dropdownTo =
86
163
  mobile && items.length > 1
87
164
  ? undefined
88
- : getVersionTargetDoc(dropdownVersion, activeDocContext).path;
165
+ : getVersionTargetDoc(displayedVersionItem.version, activeDocContext)
166
+ .path;
89
167
 
90
168
  // We don't want to render a version dropdown with 0 or 1 item. If we build
91
169
  // the site with a single docs version (onlyIncludeVersions: ['1.0.0']),
@@ -0,0 +1,86 @@
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, {useState, useRef, useEffect, type ReactNode} from 'react';
9
+ import clsx from 'clsx';
10
+ import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
11
+ import NavbarItem from '@theme/NavbarItem';
12
+ import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Desktop';
13
+
14
+ export default function DropdownNavbarItemDesktop({
15
+ items,
16
+ position,
17
+ className,
18
+ onClick,
19
+ ...props
20
+ }: Props): ReactNode {
21
+ const dropdownRef = useRef<HTMLDivElement>(null);
22
+ const [showDropdown, setShowDropdown] = useState(false);
23
+
24
+ useEffect(() => {
25
+ const handleClickOutside = (
26
+ event: MouseEvent | TouchEvent | FocusEvent,
27
+ ) => {
28
+ if (
29
+ !dropdownRef.current ||
30
+ dropdownRef.current.contains(event.target as Node)
31
+ ) {
32
+ return;
33
+ }
34
+ setShowDropdown(false);
35
+ };
36
+
37
+ document.addEventListener('mousedown', handleClickOutside);
38
+ document.addEventListener('touchstart', handleClickOutside);
39
+ document.addEventListener('focusin', handleClickOutside);
40
+
41
+ return () => {
42
+ document.removeEventListener('mousedown', handleClickOutside);
43
+ document.removeEventListener('touchstart', handleClickOutside);
44
+ document.removeEventListener('focusin', handleClickOutside);
45
+ };
46
+ }, [dropdownRef]);
47
+
48
+ return (
49
+ <div
50
+ ref={dropdownRef}
51
+ className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
52
+ 'dropdown--right': position === 'right',
53
+ 'dropdown--show': showDropdown,
54
+ })}>
55
+ <NavbarNavLink
56
+ aria-haspopup="true"
57
+ aria-expanded={showDropdown}
58
+ role="button"
59
+ // # hash permits to make the <a> tag focusable in case no link target
60
+ // See https://github.com/facebook/docusaurus/pull/6003
61
+ // There's probably a better solution though...
62
+ href={props.to ? undefined : '#'}
63
+ className={clsx('navbar__link', className)}
64
+ {...props}
65
+ onClick={props.to ? undefined : (e) => e.preventDefault()}
66
+ onKeyDown={(e) => {
67
+ if (e.key === 'Enter') {
68
+ e.preventDefault();
69
+ setShowDropdown(!showDropdown);
70
+ }
71
+ }}>
72
+ {props.children ?? props.label}
73
+ </NavbarNavLink>
74
+ <ul className="dropdown__menu">
75
+ {items.map((childItemProps, i) => (
76
+ <NavbarItem
77
+ isDropdownItem
78
+ activeClassName="dropdown__link--active"
79
+ {...childItemProps}
80
+ key={i}
81
+ />
82
+ ))}
83
+ </ul>
84
+ </div>
85
+ );
86
+ }
@@ -0,0 +1,166 @@
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, {useEffect, type ReactNode, type ComponentProps} from 'react';
9
+ import clsx from 'clsx';
10
+ import {
11
+ isRegexpStringMatch,
12
+ useCollapsible,
13
+ Collapsible,
14
+ } from '@docusaurus/theme-common';
15
+ import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal';
16
+ import {translate} from '@docusaurus/Translate';
17
+ import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
18
+ import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem';
19
+ import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Mobile';
20
+ import styles from './styles.module.css';
21
+
22
+ function isItemActive(
23
+ item: LinkLikeNavbarItemProps,
24
+ localPathname: string,
25
+ ): boolean {
26
+ if (isSamePath(item.to, localPathname)) {
27
+ return true;
28
+ }
29
+ if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
30
+ return true;
31
+ }
32
+ if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+
38
+ function containsActiveItems(
39
+ items: readonly LinkLikeNavbarItemProps[],
40
+ localPathname: string,
41
+ ): boolean {
42
+ return items.some((item) => isItemActive(item, localPathname));
43
+ }
44
+
45
+ function CollapseButton({
46
+ collapsed,
47
+ onClick,
48
+ }: {
49
+ collapsed: boolean;
50
+ onClick: ComponentProps<'button'>['onClick'];
51
+ }) {
52
+ return (
53
+ <button
54
+ aria-label={
55
+ collapsed
56
+ ? translate({
57
+ id: 'theme.navbar.mobileDropdown.collapseButton.expandAriaLabel',
58
+ message: 'Expand the dropdown',
59
+ description:
60
+ 'The ARIA label of the button to expand the mobile dropdown navbar item',
61
+ })
62
+ : translate({
63
+ id: 'theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel',
64
+ message: 'Collapse the dropdown',
65
+ description:
66
+ 'The ARIA label of the button to collapse the mobile dropdown navbar item',
67
+ })
68
+ }
69
+ aria-expanded={!collapsed}
70
+ type="button"
71
+ className="clean-btn menu__caret"
72
+ onClick={onClick}
73
+ />
74
+ );
75
+ }
76
+
77
+ function useItemCollapsible({active}: {active: boolean}) {
78
+ const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({
79
+ initialState: () => !active,
80
+ });
81
+
82
+ // Expand if any item active after a navigation
83
+ useEffect(() => {
84
+ if (active) {
85
+ setCollapsed(false);
86
+ }
87
+ }, [active, setCollapsed]);
88
+
89
+ return {
90
+ collapsed,
91
+ toggleCollapsed,
92
+ };
93
+ }
94
+
95
+ export default function DropdownNavbarItemMobile({
96
+ items,
97
+ className,
98
+ position, // Need to destructure position from props so that it doesn't get passed on.
99
+ onClick,
100
+ ...props
101
+ }: Props): ReactNode {
102
+ const localPathname = useLocalPathname();
103
+ const isActive = isSamePath(props.to, localPathname);
104
+ const containsActive = containsActiveItems(items, localPathname);
105
+
106
+ const {collapsed, toggleCollapsed} = useItemCollapsible({
107
+ active: isActive || containsActive,
108
+ });
109
+
110
+ // # hash permits to make the <a> tag focusable in case no link target
111
+ // See https://github.com/facebook/docusaurus/pull/6003
112
+ // There's probably a better solution though...
113
+ const href = props.to ? undefined : '#';
114
+
115
+ return (
116
+ <li
117
+ className={clsx('menu__list-item', {
118
+ 'menu__list-item--collapsed': collapsed,
119
+ })}>
120
+ <div
121
+ className={clsx('menu__list-item-collapsible', {
122
+ 'menu__list-item-collapsible--active': isActive,
123
+ })}>
124
+ <NavbarNavLink
125
+ role="button"
126
+ className={clsx(
127
+ styles.dropdownNavbarItemMobile,
128
+ 'menu__link menu__link--sublist',
129
+ className,
130
+ )}
131
+ href={href}
132
+ {...props}
133
+ onClick={(e) => {
134
+ // Prevent navigation when link is "#"
135
+ if (href === '#') {
136
+ e.preventDefault();
137
+ }
138
+ // Otherwise we let navigation eventually happen, and/or collapse
139
+ toggleCollapsed();
140
+ }}>
141
+ {props.children ?? props.label}
142
+ </NavbarNavLink>
143
+ <CollapseButton
144
+ collapsed={collapsed}
145
+ onClick={(e) => {
146
+ e.preventDefault();
147
+ toggleCollapsed();
148
+ }}
149
+ />
150
+ </div>
151
+
152
+ <Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
153
+ {items.map((childItemProps, i) => (
154
+ <NavbarItem
155
+ mobile
156
+ isDropdownItem
157
+ onClick={onClick}
158
+ activeClassName="menu__link--active"
159
+ {...childItemProps}
160
+ key={i}
161
+ />
162
+ ))}
163
+ </Collapsible>
164
+ </li>
165
+ );
166
+ }