@docusaurus/theme-common 2.0.0-beta.16 → 2.0.0-beta.19

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 (279) hide show
  1. package/Details.d.ts +14 -0
  2. package/lib/components/Collapsible/index.d.ts +32 -4
  3. package/lib/components/Collapsible/index.d.ts.map +1 -1
  4. package/lib/components/Collapsible/index.js +18 -10
  5. package/lib/components/Collapsible/index.js.map +1 -1
  6. package/lib/components/Details/index.d.ts +6 -1
  7. package/lib/components/Details/index.d.ts.map +1 -1
  8. package/lib/components/Details/index.js +11 -6
  9. package/lib/components/Details/index.js.map +1 -1
  10. package/lib/components/Details/styles.module.css +4 -0
  11. package/lib/{utils/announcementBarUtils.d.ts → contexts/announcementBar.d.ts} +7 -3
  12. package/lib/contexts/announcementBar.d.ts.map +1 -0
  13. package/lib/{utils/announcementBarUtils.js → contexts/announcementBar.js} +15 -14
  14. package/lib/contexts/announcementBar.js.map +1 -0
  15. package/lib/contexts/colorMode.d.ts +27 -0
  16. package/lib/contexts/colorMode.d.ts.map +1 -0
  17. package/lib/contexts/colorMode.js +132 -0
  18. package/lib/contexts/colorMode.js.map +1 -0
  19. package/lib/contexts/docSidebarItemsExpandedState.d.ts +31 -0
  20. package/lib/contexts/docSidebarItemsExpandedState.d.ts.map +1 -0
  21. package/lib/{utils → contexts}/docSidebarItemsExpandedState.js +10 -4
  22. package/lib/contexts/docSidebarItemsExpandedState.js.map +1 -0
  23. package/lib/contexts/docsPreferredVersion.d.ts +31 -0
  24. package/lib/contexts/docsPreferredVersion.d.ts.map +1 -0
  25. package/lib/contexts/docsPreferredVersion.js +128 -0
  26. package/lib/contexts/docsPreferredVersion.js.map +1 -0
  27. package/lib/contexts/docsSidebar.d.ts +26 -0
  28. package/lib/contexts/docsSidebar.d.ts.map +1 -0
  29. package/lib/contexts/docsSidebar.js +30 -0
  30. package/lib/contexts/docsSidebar.js.map +1 -0
  31. package/lib/contexts/docsVersion.d.ts +20 -0
  32. package/lib/contexts/docsVersion.d.ts.map +1 -0
  33. package/lib/contexts/docsVersion.js +26 -0
  34. package/lib/contexts/docsVersion.js.map +1 -0
  35. package/lib/contexts/navbarMobileSidebar.d.ts +31 -0
  36. package/lib/contexts/navbarMobileSidebar.d.ts.map +1 -0
  37. package/lib/contexts/navbarMobileSidebar.js +56 -0
  38. package/lib/contexts/navbarMobileSidebar.js.map +1 -0
  39. package/lib/contexts/navbarSecondaryMenu/content.d.ts +37 -0
  40. package/lib/contexts/navbarSecondaryMenu/content.d.ts.map +1 -0
  41. package/lib/contexts/navbarSecondaryMenu/content.js +56 -0
  42. package/lib/contexts/navbarSecondaryMenu/content.js.map +1 -0
  43. package/lib/contexts/navbarSecondaryMenu/display.d.ts +24 -0
  44. package/lib/contexts/navbarSecondaryMenu/display.d.ts.map +1 -0
  45. package/lib/contexts/navbarSecondaryMenu/display.js +62 -0
  46. package/lib/contexts/navbarSecondaryMenu/display.js.map +1 -0
  47. package/lib/{utils/tabGroupChoiceUtils.d.ts → contexts/tabGroupChoice.d.ts} +5 -3
  48. package/lib/contexts/tabGroupChoice.d.ts.map +1 -0
  49. package/lib/{utils/tabGroupChoiceUtils.js → contexts/tabGroupChoice.js} +15 -20
  50. package/lib/contexts/tabGroupChoice.js.map +1 -0
  51. package/lib/hooks/useBackToTopButton.d.ts +27 -0
  52. package/lib/hooks/useBackToTopButton.d.ts.map +1 -0
  53. package/lib/hooks/useBackToTopButton.js +50 -0
  54. package/lib/hooks/useBackToTopButton.js.map +1 -0
  55. package/lib/hooks/useCodeWordWrap.d.ts +14 -0
  56. package/lib/hooks/useCodeWordWrap.d.ts.map +1 -0
  57. package/lib/hooks/useCodeWordWrap.js +41 -0
  58. package/lib/hooks/useCodeWordWrap.js.map +1 -0
  59. package/lib/hooks/useHideableNavbar.d.ts +7 -3
  60. package/lib/hooks/useHideableNavbar.d.ts.map +1 -1
  61. package/lib/hooks/useHideableNavbar.js +10 -9
  62. package/lib/hooks/useHideableNavbar.js.map +1 -1
  63. package/lib/hooks/useKeyboardNavigation.d.ts +11 -1
  64. package/lib/hooks/useKeyboardNavigation.d.ts.map +1 -1
  65. package/lib/hooks/useKeyboardNavigation.js +11 -3
  66. package/lib/hooks/useKeyboardNavigation.js.map +1 -1
  67. package/lib/hooks/useLockBodyScroll.d.ts +5 -1
  68. package/lib/hooks/useLockBodyScroll.d.ts.map +1 -1
  69. package/lib/hooks/useLockBodyScroll.js +5 -1
  70. package/lib/hooks/useLockBodyScroll.js.map +1 -1
  71. package/lib/hooks/usePrismTheme.d.ts +6 -2
  72. package/lib/hooks/usePrismTheme.d.ts.map +1 -1
  73. package/lib/hooks/usePrismTheme.js +9 -6
  74. package/lib/hooks/usePrismTheme.js.map +1 -1
  75. package/lib/hooks/useSearchPage.d.ts +15 -4
  76. package/lib/hooks/useSearchPage.d.ts.map +1 -1
  77. package/lib/hooks/useSearchPage.js +4 -4
  78. package/lib/hooks/useSearchPage.js.map +1 -1
  79. package/lib/hooks/useSkipToContent.d.ts +25 -0
  80. package/lib/hooks/useSkipToContent.d.ts.map +1 -0
  81. package/lib/hooks/useSkipToContent.js +35 -0
  82. package/lib/hooks/useSkipToContent.js.map +1 -0
  83. package/lib/hooks/useTOCHighlight.d.ts +25 -0
  84. package/lib/hooks/useTOCHighlight.d.ts.map +1 -0
  85. package/lib/{utils → hooks}/useTOCHighlight.js +18 -16
  86. package/lib/hooks/useTOCHighlight.js.map +1 -0
  87. package/lib/hooks/useWindowSize.d.ts +14 -1
  88. package/lib/hooks/useWindowSize.d.ts.map +1 -1
  89. package/lib/hooks/useWindowSize.js +14 -11
  90. package/lib/hooks/useWindowSize.js.map +1 -1
  91. package/lib/index.d.ts +31 -32
  92. package/lib/index.d.ts.map +1 -1
  93. package/lib/index.js +31 -26
  94. package/lib/index.js.map +1 -1
  95. package/lib/utils/ThemeClassNames.d.ts +7 -0
  96. package/lib/utils/ThemeClassNames.d.ts.map +1 -1
  97. package/lib/utils/ThemeClassNames.js +7 -4
  98. package/lib/utils/ThemeClassNames.js.map +1 -1
  99. package/lib/utils/codeBlockUtils.d.ts +57 -4
  100. package/lib/utils/codeBlockUtils.d.ts.map +1 -1
  101. package/lib/utils/codeBlockUtils.js +111 -80
  102. package/lib/utils/codeBlockUtils.js.map +1 -1
  103. package/lib/utils/docsUtils.d.ts +83 -18
  104. package/lib/utils/docsUtils.d.ts.map +1 -1
  105. package/lib/utils/docsUtils.js +143 -62
  106. package/lib/utils/docsUtils.js.map +1 -1
  107. package/lib/utils/footerUtils.d.ts +13 -0
  108. package/lib/utils/footerUtils.d.ts.map +1 -0
  109. package/lib/utils/footerUtils.js +14 -0
  110. package/lib/utils/footerUtils.js.map +1 -0
  111. package/lib/utils/generalUtils.d.ts +4 -1
  112. package/lib/utils/generalUtils.d.ts.map +1 -1
  113. package/lib/utils/generalUtils.js +6 -3
  114. package/lib/utils/generalUtils.js.map +1 -1
  115. package/lib/utils/historyUtils.d.ts +1 -7
  116. package/lib/utils/historyUtils.d.ts.map +1 -1
  117. package/lib/utils/historyUtils.js +10 -13
  118. package/lib/utils/historyUtils.js.map +1 -1
  119. package/lib/utils/jsUtils.d.ts +1 -1
  120. package/lib/utils/jsUtils.js +1 -1
  121. package/lib/utils/metadataUtils.d.ts +38 -0
  122. package/lib/utils/metadataUtils.d.ts.map +1 -0
  123. package/lib/utils/metadataUtils.js +61 -0
  124. package/lib/utils/metadataUtils.js.map +1 -0
  125. package/lib/utils/navbarUtils.d.ts +21 -0
  126. package/lib/utils/navbarUtils.d.ts.map +1 -0
  127. package/lib/utils/navbarUtils.js +32 -0
  128. package/lib/utils/navbarUtils.js.map +1 -0
  129. package/lib/utils/reactUtils.d.ts +21 -7
  130. package/lib/utils/reactUtils.d.ts.map +1 -1
  131. package/lib/utils/reactUtils.js +34 -6
  132. package/lib/utils/reactUtils.js.map +1 -1
  133. package/lib/utils/regexpUtils.d.ts +2 -1
  134. package/lib/utils/regexpUtils.d.ts.map +1 -1
  135. package/lib/utils/regexpUtils.js +2 -1
  136. package/lib/utils/regexpUtils.js.map +1 -1
  137. package/lib/utils/routesUtils.d.ts +19 -4
  138. package/lib/utils/routesUtils.d.ts.map +1 -1
  139. package/lib/utils/routesUtils.js +40 -19
  140. package/lib/utils/routesUtils.js.map +1 -1
  141. package/lib/utils/scrollUtils.d.ts +55 -25
  142. package/lib/utils/scrollUtils.d.ts.map +1 -1
  143. package/lib/utils/scrollUtils.js +85 -21
  144. package/lib/utils/scrollUtils.js.map +1 -1
  145. package/lib/utils/searchUtils.d.ts +12 -0
  146. package/lib/utils/searchUtils.d.ts.map +1 -1
  147. package/lib/utils/searchUtils.js +36 -0
  148. package/lib/utils/searchUtils.js.map +1 -1
  149. package/lib/utils/storageUtils.d.ts +12 -9
  150. package/lib/utils/storageUtils.d.ts.map +1 -1
  151. package/lib/utils/storageUtils.js +21 -13
  152. package/lib/utils/storageUtils.js.map +1 -1
  153. package/lib/utils/tagsUtils.d.ts +7 -8
  154. package/lib/utils/tagsUtils.d.ts.map +1 -1
  155. package/lib/utils/tagsUtils.js +8 -6
  156. package/lib/utils/tagsUtils.js.map +1 -1
  157. package/lib/utils/tocUtils.d.ts +17 -1
  158. package/lib/utils/tocUtils.d.ts.map +1 -1
  159. package/lib/utils/tocUtils.js +20 -9
  160. package/lib/utils/tocUtils.js.map +1 -1
  161. package/lib/utils/useAlternatePageUtils.d.ts +20 -1
  162. package/lib/utils/useAlternatePageUtils.d.ts.map +1 -1
  163. package/lib/utils/useAlternatePageUtils.js +6 -3
  164. package/lib/utils/useAlternatePageUtils.js.map +1 -1
  165. package/lib/utils/useLocalPathname.d.ts +5 -0
  166. package/lib/utils/useLocalPathname.d.ts.map +1 -1
  167. package/lib/utils/useLocalPathname.js +6 -4
  168. package/lib/utils/useLocalPathname.js.map +1 -1
  169. package/lib/utils/useLocationChange.d.ts +7 -5
  170. package/lib/utils/useLocationChange.d.ts.map +1 -1
  171. package/lib/utils/useLocationChange.js +6 -2
  172. package/lib/utils/useLocationChange.js.map +1 -1
  173. package/lib/utils/usePluralForm.d.ts +11 -0
  174. package/lib/utils/usePluralForm.d.ts.map +1 -1
  175. package/lib/utils/usePluralForm.js +19 -24
  176. package/lib/utils/usePluralForm.js.map +1 -1
  177. package/lib/utils/useThemeConfig.d.ts +28 -22
  178. package/lib/utils/useThemeConfig.d.ts.map +1 -1
  179. package/lib/utils/useThemeConfig.js +3 -0
  180. package/lib/utils/useThemeConfig.js.map +1 -1
  181. package/package.json +18 -11
  182. package/src/components/Collapsible/index.tsx +42 -24
  183. package/src/components/Details/index.tsx +15 -8
  184. package/src/components/Details/styles.module.css +4 -0
  185. package/src/{utils/announcementBarUtils.tsx → contexts/announcementBar.tsx} +20 -22
  186. package/src/contexts/colorMode.tsx +199 -0
  187. package/src/contexts/docSidebarItemsExpandedState.tsx +55 -0
  188. package/src/contexts/docsPreferredVersion.tsx +253 -0
  189. package/src/contexts/docsSidebar.tsx +50 -0
  190. package/src/contexts/docsVersion.tsx +36 -0
  191. package/src/contexts/navbarMobileSidebar.tsx +99 -0
  192. package/src/contexts/navbarSecondaryMenu/content.tsx +110 -0
  193. package/src/contexts/navbarSecondaryMenu/display.tsx +102 -0
  194. package/src/{utils/tabGroupChoiceUtils.tsx → contexts/tabGroupChoice.tsx} +28 -33
  195. package/src/hooks/useBackToTopButton.ts +73 -0
  196. package/src/hooks/useCodeWordWrap.ts +56 -0
  197. package/src/hooks/useHideableNavbar.ts +12 -14
  198. package/src/hooks/useKeyboardNavigation.ts +11 -3
  199. package/src/hooks/useLockBodyScroll.ts +5 -2
  200. package/src/hooks/usePrismTheme.ts +10 -6
  201. package/src/hooks/useSearchPage.ts +18 -5
  202. package/src/hooks/useSkipToContent.ts +58 -0
  203. package/src/{utils → hooks}/useTOCHighlight.ts +24 -15
  204. package/src/hooks/useWindowSize.ts +14 -12
  205. package/src/index.ts +77 -57
  206. package/src/utils/ThemeClassNames.ts +10 -6
  207. package/src/utils/codeBlockUtils.ts +178 -92
  208. package/src/utils/docsUtils.tsx +205 -101
  209. package/src/utils/footerUtils.ts +18 -0
  210. package/src/utils/generalUtils.ts +6 -3
  211. package/src/utils/historyUtils.ts +11 -17
  212. package/src/utils/jsUtils.ts +1 -1
  213. package/src/utils/metadataUtils.tsx +115 -0
  214. package/src/utils/navbarUtils.tsx +45 -0
  215. package/src/utils/reactUtils.tsx +40 -7
  216. package/src/utils/regexpUtils.ts +2 -1
  217. package/src/utils/routesUtils.ts +58 -22
  218. package/src/utils/scrollUtils.tsx +120 -49
  219. package/src/utils/searchUtils.ts +51 -0
  220. package/src/utils/storageUtils.ts +23 -15
  221. package/src/utils/tagsUtils.ts +12 -10
  222. package/src/utils/tocUtils.ts +22 -11
  223. package/src/utils/useAlternatePageUtils.ts +17 -5
  224. package/src/utils/useLocalPathname.ts +6 -4
  225. package/src/utils/useLocationChange.ts +12 -10
  226. package/src/utils/usePluralForm.ts +27 -24
  227. package/src/utils/useThemeConfig.ts +23 -22
  228. package/yarn-error.log +20199 -0
  229. package/lib/utils/announcementBarUtils.d.ts.map +0 -1
  230. package/lib/utils/announcementBarUtils.js.map +0 -1
  231. package/lib/utils/colorModeUtils.d.ts +0 -18
  232. package/lib/utils/colorModeUtils.d.ts.map +0 -1
  233. package/lib/utils/colorModeUtils.js +0 -106
  234. package/lib/utils/colorModeUtils.js.map +0 -1
  235. package/lib/utils/docSidebarItemsExpandedState.d.ts +0 -17
  236. package/lib/utils/docSidebarItemsExpandedState.d.ts.map +0 -1
  237. package/lib/utils/docSidebarItemsExpandedState.js.map +0 -1
  238. package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.d.ts +0 -22
  239. package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.d.ts.map +0 -1
  240. package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.js +0 -91
  241. package/lib/utils/docsPreferredVersion/DocsPreferredVersionProvider.js.map +0 -1
  242. package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.d.ts +0 -14
  243. package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.d.ts.map +0 -1
  244. package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.js +0 -19
  245. package/lib/utils/docsPreferredVersion/DocsPreferredVersionStorage.js.map +0 -1
  246. package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.d.ts +0 -14
  247. package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.d.ts.map +0 -1
  248. package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.js +0 -41
  249. package/lib/utils/docsPreferredVersion/useDocsPreferredVersion.js.map +0 -1
  250. package/lib/utils/mobileSecondaryMenu.d.ts +0 -21
  251. package/lib/utils/mobileSecondaryMenu.d.ts.map +0 -1
  252. package/lib/utils/mobileSecondaryMenu.js +0 -50
  253. package/lib/utils/mobileSecondaryMenu.js.map +0 -1
  254. package/lib/utils/pathUtils.d.ts +0 -8
  255. package/lib/utils/pathUtils.d.ts.map +0 -1
  256. package/lib/utils/pathUtils.js +0 -17
  257. package/lib/utils/pathUtils.js.map +0 -1
  258. package/lib/utils/tabGroupChoiceUtils.d.ts.map +0 -1
  259. package/lib/utils/tabGroupChoiceUtils.js.map +0 -1
  260. package/lib/utils/useContextualSearchFilters.d.ts +0 -12
  261. package/lib/utils/useContextualSearchFilters.d.ts.map +0 -1
  262. package/lib/utils/useContextualSearchFilters.js +0 -37
  263. package/lib/utils/useContextualSearchFilters.js.map +0 -1
  264. package/lib/utils/usePrevious.d.ts +0 -8
  265. package/lib/utils/usePrevious.d.ts.map +0 -1
  266. package/lib/utils/usePrevious.js +0 -16
  267. package/lib/utils/usePrevious.js.map +0 -1
  268. package/lib/utils/useTOCHighlight.d.ts +0 -14
  269. package/lib/utils/useTOCHighlight.d.ts.map +0 -1
  270. package/lib/utils/useTOCHighlight.js.map +0 -1
  271. package/src/utils/colorModeUtils.tsx +0 -156
  272. package/src/utils/docSidebarItemsExpandedState.tsx +0 -41
  273. package/src/utils/docsPreferredVersion/DocsPreferredVersionProvider.tsx +0 -167
  274. package/src/utils/docsPreferredVersion/DocsPreferredVersionStorage.ts +0 -33
  275. package/src/utils/docsPreferredVersion/useDocsPreferredVersion.ts +0 -70
  276. package/src/utils/mobileSecondaryMenu.tsx +0 -115
  277. package/src/utils/pathUtils.ts +0 -19
  278. package/src/utils/useContextualSearchFilters.ts +0 -53
  279. package/src/utils/usePrevious.ts +0 -19
@@ -5,56 +5,42 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import React, {createContext, type ReactNode, useContext} from 'react';
8
+ import {useMemo} from 'react';
9
9
  import {
10
- useActivePlugin,
11
10
  useAllDocsData,
11
+ useActivePlugin,
12
+ useActiveDocContext,
13
+ useLatestVersion,
14
+ type GlobalVersion,
15
+ type GlobalSidebar,
16
+ type GlobalDoc,
12
17
  } from '@docusaurus/plugin-content-docs/client';
13
18
  import type {
14
19
  PropSidebar,
15
20
  PropSidebarItem,
16
21
  PropSidebarItemCategory,
17
22
  PropVersionDoc,
18
- PropVersionMetadata,
19
23
  PropSidebarBreadcrumbsItem,
20
24
  } from '@docusaurus/plugin-content-docs';
21
- import {isSamePath} from './pathUtils';
22
- import {useLocation} from '@docusaurus/router';
25
+ import type {Props as DocPageProps} from '@theme/DocPage';
26
+ import {useDocsPreferredVersion} from '../contexts/docsPreferredVersion';
27
+ import {useDocsVersion} from '../contexts/docsVersion';
28
+ import {useDocsSidebar} from '../contexts/docsSidebar';
29
+ import {uniq} from './jsUtils';
30
+ import {isSamePath} from './routesUtils';
31
+ import {matchPath, useLocation} from '@docusaurus/router';
32
+ import renderRoutes from '@docusaurus/renderRoutes';
23
33
 
24
34
  // TODO not ideal, see also "useDocs"
25
35
  export const isDocsPluginEnabled: boolean = !!useAllDocsData;
26
36
 
27
- // Using a Symbol because null is a valid context value (a doc with no sidebar)
28
- // Inspired by https://github.com/jamiebuilds/unstated-next/blob/master/src/unstated-next.tsx
29
- const EmptyContextValue: unique symbol = Symbol('EmptyContext');
30
-
31
- const DocsVersionContext = createContext<
32
- PropVersionMetadata | typeof EmptyContextValue
33
- >(EmptyContextValue);
34
-
35
- export function DocsVersionProvider({
36
- children,
37
- version,
38
- }: {
39
- children: ReactNode;
40
- version: PropVersionMetadata | typeof EmptyContextValue;
41
- }): JSX.Element {
42
- return (
43
- <DocsVersionContext.Provider value={version}>
44
- {children}
45
- </DocsVersionContext.Provider>
46
- );
47
- }
48
-
49
- export function useDocsVersion(): PropVersionMetadata {
50
- const version = useContext(DocsVersionContext);
51
- if (version === EmptyContextValue) {
52
- throw new Error('This hook requires usage of <DocsVersionProvider>');
53
- }
54
- return version;
55
- }
56
-
37
+ /**
38
+ * A null-safe way to access a doc's data by ID in the active version.
39
+ */
57
40
  export function useDocById(id: string): PropVersionDoc;
41
+ /**
42
+ * A null-safe way to access a doc's data by ID in the active version.
43
+ */
58
44
  export function useDocById(id: string | undefined): PropVersionDoc | undefined;
59
45
  export function useDocById(id: string | undefined): PropVersionDoc | undefined {
60
46
  const version = useDocsVersion();
@@ -68,34 +54,9 @@ export function useDocById(id: string | undefined): PropVersionDoc | undefined {
68
54
  return doc;
69
55
  }
70
56
 
71
- const DocsSidebarContext = createContext<
72
- PropSidebar | null | typeof EmptyContextValue
73
- >(EmptyContextValue);
74
-
75
- export function DocsSidebarProvider({
76
- children,
77
- sidebar,
78
- }: {
79
- children: ReactNode;
80
- sidebar: PropSidebar | null;
81
- }): JSX.Element {
82
- return (
83
- <DocsSidebarContext.Provider value={sidebar}>
84
- {children}
85
- </DocsSidebarContext.Provider>
86
- );
87
- }
88
-
89
- export function useDocsSidebar(): PropSidebar | null {
90
- const sidebar = useContext(DocsSidebarContext);
91
- if (sidebar === EmptyContextValue) {
92
- throw new Error('This hook requires usage of <DocsSidebarProvider>');
93
- }
94
- return sidebar;
95
- }
96
-
97
- // Use the components props and the sidebar in context
98
- // to get back the related sidebar category that we want to render
57
+ /**
58
+ * Pure function, similar to `Array#find`, but works on the sidebar tree.
59
+ */
99
60
  export function findSidebarCategory(
100
61
  sidebar: PropSidebar,
101
62
  predicate: (category: PropSidebarItemCategory) => boolean,
@@ -114,7 +75,10 @@ export function findSidebarCategory(
114
75
  return undefined;
115
76
  }
116
77
 
117
- // If a category card has no link => link to the first subItem having a link
78
+ /**
79
+ * Best effort to assign a link to a sidebar category. If the category doesn't
80
+ * have a link itself, we link to the first sub item with a link.
81
+ */
118
82
  export function findFirstCategoryLink(
119
83
  item: PropSidebarItemCategory,
120
84
  ): string | undefined {
@@ -130,101 +94,241 @@ export function findFirstCategoryLink(
130
94
  if (categoryLink) {
131
95
  return categoryLink;
132
96
  }
133
- } else if (subItem.type === 'html') {
134
- // skip
135
- } else {
136
- throw new Error(
137
- `Unexpected category item type for ${JSON.stringify(subItem)}`,
138
- );
139
97
  }
98
+ // Could be "html" items
140
99
  }
141
100
  return undefined;
142
101
  }
143
102
 
103
+ /**
104
+ * Gets the category associated with the current location. Should only be used
105
+ * on category index pages.
106
+ */
144
107
  export function useCurrentSidebarCategory(): PropSidebarItemCategory {
145
108
  const {pathname} = useLocation();
146
109
  const sidebar = useDocsSidebar();
147
110
  if (!sidebar) {
148
111
  throw new Error('Unexpected: cant find current sidebar in context');
149
112
  }
150
- const category = findSidebarCategory(sidebar, (item) =>
113
+ const category = findSidebarCategory(sidebar.items, (item) =>
151
114
  isSamePath(item.href, pathname),
152
115
  );
153
116
  if (!category) {
154
117
  throw new Error(
155
- `Unexpected: sidebar category could not be found for pathname='${pathname}'.
156
- Hook useCurrentSidebarCategory() should only be used on Category pages`,
118
+ `${pathname} is not associated with a category. useCurrentSidebarCategory() should only be used on category index pages.`,
157
119
  );
158
120
  }
159
121
  return category;
160
122
  }
161
123
 
162
- function containsActiveSidebarItem(
124
+ const isActive = (testedPath: string | undefined, activePath: string) =>
125
+ typeof testedPath !== 'undefined' && isSamePath(testedPath, activePath);
126
+ const containsActiveSidebarItem = (
163
127
  items: PropSidebarItem[],
164
128
  activePath: string,
165
- ): boolean {
166
- return items.some((subItem) => isActiveSidebarItem(subItem, activePath));
167
- }
129
+ ) => items.some((subItem) => isActiveSidebarItem(subItem, activePath));
168
130
 
131
+ /**
132
+ * Checks if a sidebar item should be active, based on the active path.
133
+ */
169
134
  export function isActiveSidebarItem(
170
135
  item: PropSidebarItem,
171
136
  activePath: string,
172
137
  ): boolean {
173
- const isActive = (testedPath: string | undefined) =>
174
- typeof testedPath !== 'undefined' && isSamePath(testedPath, activePath);
175
-
176
138
  if (item.type === 'link') {
177
- return isActive(item.href);
139
+ return isActive(item.href, activePath);
178
140
  }
179
141
 
180
142
  if (item.type === 'category') {
181
143
  return (
182
- isActive(item.href) || containsActiveSidebarItem(item.items, activePath)
144
+ isActive(item.href, activePath) ||
145
+ containsActiveSidebarItem(item.items, activePath)
183
146
  );
184
147
  }
185
148
 
186
149
  return false;
187
150
  }
188
151
 
189
- export function getBreadcrumbs({
190
- sidebar,
191
- pathname,
192
- }: {
193
- sidebar: PropSidebar;
194
- pathname: string;
195
- }): PropSidebarBreadcrumbsItem[] {
152
+ /**
153
+ * Gets the breadcrumbs of the current doc page, based on its sidebar location.
154
+ * Returns `null` if there's no sidebar or breadcrumbs are disabled.
155
+ */
156
+ export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null {
157
+ const sidebar = useDocsSidebar();
158
+ const {pathname} = useLocation();
159
+ const breadcrumbsOption = useActivePlugin()?.pluginData.breadcrumbs;
160
+
161
+ if (breadcrumbsOption === false || !sidebar) {
162
+ return null;
163
+ }
164
+
196
165
  const breadcrumbs: PropSidebarBreadcrumbsItem[] = [];
197
166
 
198
167
  function extract(items: PropSidebar) {
199
168
  for (const item of items) {
200
169
  if (
201
- item.type === 'category' &&
202
- (isSamePath(item.href, pathname) || extract(item.items))
170
+ (item.type === 'category' &&
171
+ (isSamePath(item.href, pathname) || extract(item.items))) ||
172
+ (item.type === 'link' && isSamePath(item.href, pathname))
203
173
  ) {
204
174
  breadcrumbs.push(item);
205
175
  return true;
206
- } else if (item.type === 'link' && isSamePath(item.href, pathname)) {
207
- breadcrumbs.push(item);
208
- return true;
209
176
  }
210
177
  }
211
178
 
212
179
  return false;
213
180
  }
214
181
 
215
- extract(sidebar);
182
+ extract(sidebar.items);
216
183
 
217
184
  return breadcrumbs.reverse();
218
185
  }
219
186
 
220
- export function useSidebarBreadcrumbs(): PropSidebarBreadcrumbsItem[] | null {
221
- const sidebar = useDocsSidebar();
222
- const {pathname} = useLocation();
223
- const breadcrumbsOption = useActivePlugin()?.pluginData.breadcrumbs;
187
+ /**
188
+ * "Version candidates" are mostly useful for the layout components, which must
189
+ * be able to work on all pages. For example, if a user has `{ type: "doc",
190
+ * docId: "intro" }` as a navbar item, which version does that refer to? We
191
+ * believe that it could refer to at most three version candidates:
192
+ *
193
+ * 1. The **active version**, the one that the user is currently browsing. See
194
+ * {@link useActiveDocContext}.
195
+ * 2. The **preferred version**, the one that the user last visited. See
196
+ * {@link useDocsPreferredVersion}.
197
+ * 3. The **latest version**, the "default". See {@link useLatestVersion}.
198
+ *
199
+ * @param docsPluginId The plugin ID to get versions from.
200
+ * @returns An array of 1~3 versions with priorities defined above, guaranteed
201
+ * to be unique and non-sparse. Will be memoized, hence stable for deps array.
202
+ */
203
+ export function useDocsVersionCandidates(
204
+ docsPluginId?: string,
205
+ ): [GlobalVersion, ...GlobalVersion[]] {
206
+ const {activeVersion} = useActiveDocContext(docsPluginId);
207
+ const {preferredVersion} = useDocsPreferredVersion(docsPluginId);
208
+ const latestVersion = useLatestVersion(docsPluginId);
209
+ return useMemo(
210
+ () =>
211
+ uniq(
212
+ [activeVersion, preferredVersion, latestVersion].filter(Boolean),
213
+ ) as [GlobalVersion, ...GlobalVersion[]],
214
+ [activeVersion, preferredVersion, latestVersion],
215
+ );
216
+ }
224
217
 
225
- if (breadcrumbsOption === false || !sidebar) {
218
+ /**
219
+ * The layout components, like navbar items, must be able to work on all pages,
220
+ * even on non-doc ones where there's no version context, so a sidebar ID could
221
+ * be ambiguous. This hook would always return a sidebar to be linked to. See
222
+ * also {@link useDocsVersionCandidates} for how this selection is done.
223
+ *
224
+ * @throws This hook throws if a sidebar with said ID is not found.
225
+ */
226
+ export function useLayoutDocsSidebar(
227
+ sidebarId: string,
228
+ docsPluginId?: string,
229
+ ): GlobalSidebar {
230
+ const versions = useDocsVersionCandidates(docsPluginId);
231
+ return useMemo(() => {
232
+ const allSidebars = versions.flatMap((version) =>
233
+ version.sidebars ? Object.entries(version.sidebars) : [],
234
+ );
235
+ const sidebarEntry = allSidebars.find(
236
+ (sidebar) => sidebar[0] === sidebarId,
237
+ );
238
+ if (!sidebarEntry) {
239
+ throw new Error(
240
+ `Can't find any sidebar with id "${sidebarId}" in version${
241
+ versions.length > 1 ? 's' : ''
242
+ } ${versions.map((version) => version.name).join(', ')}".
243
+ Available sidebar ids are:
244
+ - ${Object.keys(allSidebars).join('\n- ')}`,
245
+ );
246
+ }
247
+ return sidebarEntry[1];
248
+ }, [sidebarId, versions]);
249
+ }
250
+
251
+ /**
252
+ * The layout components, like navbar items, must be able to work on all pages,
253
+ * even on non-doc ones where there's no version context, so a doc ID could be
254
+ * ambiguous. This hook would always return a doc to be linked to. See also
255
+ * {@link useDocsVersionCandidates} for how this selection is done.
256
+ *
257
+ * @throws This hook throws if a doc with said ID is not found.
258
+ */
259
+ export function useLayoutDoc(
260
+ docId: string,
261
+ docsPluginId?: string,
262
+ ): GlobalDoc | null {
263
+ const versions = useDocsVersionCandidates(docsPluginId);
264
+ return useMemo(() => {
265
+ const allDocs = versions.flatMap((version) => version.docs);
266
+ const doc = allDocs.find((versionDoc) => versionDoc.id === docId);
267
+ if (!doc) {
268
+ const isDraft = versions
269
+ .flatMap((version) => version.draftIds)
270
+ .includes(docId);
271
+ // Drafts should be silently filtered instead of throwing
272
+ if (isDraft) {
273
+ return null;
274
+ }
275
+ throw new Error(
276
+ `DocNavbarItem: couldn't find any doc with id "${docId}" in version${
277
+ versions.length > 1 ? 's' : ''
278
+ } ${versions.map((version) => version.name).join(', ')}".
279
+ Available doc ids are:
280
+ - ${uniq(allDocs.map((versionDoc) => versionDoc.id)).join('\n- ')}`,
281
+ );
282
+ }
283
+ return doc;
284
+ }, [docId, versions]);
285
+ }
286
+
287
+ // TODO later read version/route directly from context
288
+ /**
289
+ * The docs plugin creates nested routes, with the top-level route providing the
290
+ * version metadata, and the subroutes creating individual doc pages. This hook
291
+ * will match the current location against all known sub-routes.
292
+ *
293
+ * @param props The props received by `@theme/DocPage`
294
+ * @returns The data of the relevant document at the current location, or `null`
295
+ * if no document associated with the current location can be found.
296
+ */
297
+ export function useDocRouteMetadata({
298
+ route,
299
+ versionMetadata,
300
+ }: DocPageProps): null | {
301
+ /** The element that should be rendered at the current location. */
302
+ docElement: JSX.Element;
303
+ /**
304
+ * The name of the sidebar associated with the current doc. `sidebarName` and
305
+ * `sidebarItems` correspond to the value of {@link useDocsSidebar}.
306
+ */
307
+ sidebarName: string | undefined;
308
+ /** The items of the sidebar associated with the current doc. */
309
+ sidebarItems: PropSidebar | undefined;
310
+ } {
311
+ const location = useLocation();
312
+ const docRoutes = route.routes!;
313
+ const currentDocRoute = docRoutes.find((docRoute) =>
314
+ matchPath(location.pathname, docRoute),
315
+ );
316
+ if (!currentDocRoute) {
226
317
  return null;
227
318
  }
228
319
 
229
- return getBreadcrumbs({sidebar, pathname});
320
+ // For now, the sidebarName is added as route config: not ideal!
321
+ const sidebarName = currentDocRoute.sidebar;
322
+
323
+ const sidebarItems = sidebarName
324
+ ? versionMetadata.docsSidebars[sidebarName]
325
+ : undefined;
326
+
327
+ const docElement = renderRoutes(docRoutes, {versionMetadata});
328
+
329
+ return {
330
+ docElement,
331
+ sidebarName,
332
+ sidebarItems,
333
+ };
230
334
  }
@@ -0,0 +1,18 @@
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 type {MultiColumnFooter, SimpleFooter} from './useThemeConfig';
9
+
10
+ /**
11
+ * A rough duck-typing about whether the `footer.links` is intended to be multi-
12
+ * column.
13
+ */
14
+ export function isMultiColumnFooterLinks(
15
+ links: MultiColumnFooter['links'] | SimpleFooter['links'],
16
+ ): links is MultiColumnFooter['links'] {
17
+ return 'title' in links[0]!;
18
+ }
@@ -7,10 +7,13 @@
7
7
 
8
8
  import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
9
9
 
10
- export const useTitleFormatter = (title?: string | undefined): string => {
10
+ /**
11
+ * Formats the page's title based on relevant site config and other contexts.
12
+ */
13
+ export function useTitleFormatter(title?: string | undefined): string {
11
14
  const {siteConfig} = useDocusaurusContext();
12
15
  const {title: siteTitle, titleDelimiter} = siteConfig;
13
- return title && title.trim().length
16
+ return title?.trim().length
14
17
  ? `${title.trim()} ${titleDelimiter} ${siteTitle}`
15
18
  : siteTitle;
16
- };
19
+ }
@@ -5,44 +5,38 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {useEffect, useRef} from 'react';
8
+ import {useEffect} from 'react';
9
9
  import {useHistory} from '@docusaurus/router';
10
+ import {useDynamicCallback} from './reactUtils';
10
11
  import type {Location, Action} from 'history';
11
12
 
12
13
  type HistoryBlockHandler = (location: Location, action: Action) => void | false;
13
14
 
14
15
  /**
15
16
  * Permits to register a handler that will be called on history actions (pop,
16
- * push, replace) If the handler returns false, the navigation transition will
17
- * be blocked/cancelled
17
+ * push, replace). If the handler returns `false`, the navigation transition
18
+ * will be blocked/cancelled.
18
19
  */
19
- export function useHistoryActionHandler(handler: HistoryBlockHandler): void {
20
+ function useHistoryActionHandler(handler: HistoryBlockHandler): void {
20
21
  const {block} = useHistory();
21
-
22
- // Avoid stale closure issues without triggering useless re-renders
23
- const lastHandlerRef = useRef(handler);
24
- useEffect(() => {
25
- lastHandlerRef.current = handler;
26
- }, [handler]);
27
-
22
+ const stableHandler = useDynamicCallback(handler);
28
23
  useEffect(
29
- () =>
30
- // See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md
31
- block((location, action) => lastHandlerRef.current(location, action)),
32
- [block, lastHandlerRef],
24
+ // See https://github.com/remix-run/history/blob/main/docs/blocking-transitions.md
25
+ () => block((location, action) => stableHandler(location, action)),
26
+ [block, stableHandler],
33
27
  );
34
28
  }
35
29
 
36
30
  /**
37
31
  * Permits to register a handler that will be called on history pop navigation
38
- * (backward/forward) If the handler returns false, the backward/forward
32
+ * (backward/forward). If the handler returns `false`, the backward/forward
39
33
  * transition will be blocked. Unfortunately there's no good way to detect the
40
34
  * "direction" (backward/forward) of the POP event.
41
35
  */
42
36
  export function useHistoryPopHandler(handler: HistoryBlockHandler): void {
43
37
  useHistoryActionHandler((location, action) => {
44
38
  if (action === 'POP') {
45
- // Eventually block navigation if handler returns false
39
+ // Maybe block navigation if handler returns false
46
40
  return handler(location, action);
47
41
  }
48
42
  // Don't block other navigation actions
@@ -26,7 +26,7 @@ export function duplicates<T>(
26
26
  }
27
27
 
28
28
  /**
29
- * Remove duplicate array items (similar to _.uniq)
29
+ * Remove duplicate array items (similar to `_.uniq`)
30
30
  * @param arr The array.
31
31
  * @returns An array with duplicate elements removed by reference comparison.
32
32
  */
@@ -0,0 +1,115 @@
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 Head from '@docusaurus/Head';
10
+ import clsx from 'clsx';
11
+ import useRouteContext from '@docusaurus/useRouteContext';
12
+ import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
13
+ import {useTitleFormatter} from './generalUtils';
14
+
15
+ type PageMetadataProps = {
16
+ readonly title?: string;
17
+ readonly description?: string;
18
+ readonly keywords?: readonly string[] | string;
19
+ readonly image?: string;
20
+ readonly children?: ReactNode;
21
+ };
22
+
23
+ /**
24
+ * Helper component to manipulate page metadata and override site defaults.
25
+ * Works in the same way as Helmet.
26
+ */
27
+ export function PageMetadata({
28
+ title,
29
+ description,
30
+ keywords,
31
+ image,
32
+ children,
33
+ }: PageMetadataProps): JSX.Element {
34
+ const pageTitle = useTitleFormatter(title);
35
+ const {withBaseUrl} = useBaseUrlUtils();
36
+ const pageImage = image ? withBaseUrl(image, {absolute: true}) : undefined;
37
+
38
+ return (
39
+ <Head>
40
+ {title && <title>{pageTitle}</title>}
41
+ {title && <meta property="og:title" content={pageTitle} />}
42
+
43
+ {description && <meta name="description" content={description} />}
44
+ {description && <meta property="og:description" content={description} />}
45
+
46
+ {keywords && (
47
+ <meta
48
+ name="keywords"
49
+ content={
50
+ // https://github.com/microsoft/TypeScript/issues/17002
51
+ (Array.isArray(keywords) ? keywords.join(',') : keywords) as string
52
+ }
53
+ />
54
+ )}
55
+
56
+ {pageImage && <meta property="og:image" content={pageImage} />}
57
+ {pageImage && <meta name="twitter:image" content={pageImage} />}
58
+
59
+ {children}
60
+ </Head>
61
+ );
62
+ }
63
+
64
+ const HtmlClassNameContext = React.createContext<string | undefined>(undefined);
65
+
66
+ /**
67
+ * Every layer of this provider will append a class name to the HTML element.
68
+ * There's no consumer for this hook: it's side-effect-only. This wrapper is
69
+ * necessary because Helmet does not "merge" classes.
70
+ * @see https://github.com/staylor/react-helmet-async/issues/161
71
+ */
72
+ export function HtmlClassNameProvider({
73
+ className: classNameProp,
74
+ children,
75
+ }: {
76
+ className: string;
77
+ children: ReactNode;
78
+ }): JSX.Element {
79
+ const classNameContext = React.useContext(HtmlClassNameContext);
80
+ const className = clsx(classNameContext, classNameProp);
81
+ return (
82
+ <HtmlClassNameContext.Provider value={className}>
83
+ <Head>
84
+ <html className={className} />
85
+ </Head>
86
+ {children}
87
+ </HtmlClassNameContext.Provider>
88
+ );
89
+ }
90
+
91
+ function pluginNameToClassName(pluginName: string) {
92
+ return `plugin-${pluginName.replace(
93
+ /docusaurus-(?:plugin|theme)-(?:content-)?/gi,
94
+ '',
95
+ )}`;
96
+ }
97
+
98
+ /**
99
+ * A very thin wrapper around `HtmlClassNameProvider` that adds the plugin ID +
100
+ * name to the HTML class name.
101
+ */
102
+ export function PluginHtmlClassNameProvider({
103
+ children,
104
+ }: {
105
+ children: ReactNode;
106
+ }): JSX.Element {
107
+ const routeContext = useRouteContext();
108
+ const nameClass = pluginNameToClassName(routeContext.plugin.name);
109
+ const idClass = `plugin-id-${routeContext.plugin.id}`;
110
+ return (
111
+ <HtmlClassNameProvider className={clsx(nameClass, idClass)}>
112
+ {children}
113
+ </HtmlClassNameProvider>
114
+ );
115
+ }
@@ -0,0 +1,45 @@
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 {NavbarMobileSidebarProvider} from '../contexts/navbarMobileSidebar';
10
+ import {NavbarSecondaryMenuContentProvider} from '../contexts/navbarSecondaryMenu/content';
11
+ import {NavbarSecondaryMenuDisplayProvider} from '../contexts/navbarSecondaryMenu/display';
12
+
13
+ const DefaultNavItemPosition = 'right';
14
+
15
+ /**
16
+ * Split links by left/right. If position is unspecified, fallback to right.
17
+ */
18
+ export function splitNavbarItems<T extends {position?: 'left' | 'right'}>(
19
+ items: T[],
20
+ ): [leftItems: T[], rightItems: T[]] {
21
+ function isLeft(item: T): boolean {
22
+ return (item.position ?? DefaultNavItemPosition) === 'left';
23
+ }
24
+
25
+ const leftItems = items.filter(isLeft);
26
+ const rightItems = items.filter((item) => !isLeft(item));
27
+
28
+ return [leftItems, rightItems];
29
+ }
30
+
31
+ /**
32
+ * Composes multiple navbar state providers that are mutually dependent and
33
+ * hence can't be re-ordered.
34
+ */
35
+ export function NavbarProvider({children}: {children: ReactNode}): JSX.Element {
36
+ return (
37
+ <NavbarSecondaryMenuContentProvider>
38
+ <NavbarMobileSidebarProvider>
39
+ <NavbarSecondaryMenuDisplayProvider>
40
+ {children}
41
+ </NavbarSecondaryMenuDisplayProvider>
42
+ </NavbarMobileSidebarProvider>
43
+ </NavbarSecondaryMenuContentProvider>
44
+ );
45
+ }