@azerion/bunosaurus-theme-common 0.0.0-stage → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +2 -2
  2. package/lib/components/Collapsible/index.d.ts +59 -0
  3. package/lib/components/Collapsible/index.d.ts.map +1 -0
  4. package/lib/components/Collapsible/index.js +145 -0
  5. package/lib/components/Collapsible/index.js.map +1 -0
  6. package/lib/components/Details/index.d.ts +20 -0
  7. package/lib/components/Details/index.d.ts.map +1 -0
  8. package/lib/components/Details/index.js +86 -0
  9. package/lib/components/Details/index.js.map +1 -0
  10. package/lib/components/Details/styles.module.css +66 -0
  11. package/lib/components/ThemedComponent/index.d.ts +31 -0
  12. package/lib/components/ThemedComponent/index.d.ts.map +1 -0
  13. package/lib/components/ThemedComponent/index.js +47 -0
  14. package/lib/components/ThemedComponent/index.js.map +1 -0
  15. package/lib/components/ThemedComponent/styles.module.css +26 -0
  16. package/lib/contexts/announcementBar.d.ts +21 -0
  17. package/lib/contexts/announcementBar.d.ts.map +1 -0
  18. package/lib/contexts/announcementBar.js +72 -0
  19. package/lib/contexts/announcementBar.js.map +1 -0
  20. package/lib/contexts/colorMode.d.ts +23 -0
  21. package/lib/contexts/colorMode.d.ts.map +1 -0
  22. package/lib/contexts/colorMode.js +163 -0
  23. package/lib/contexts/colorMode.js.map +1 -0
  24. package/lib/contexts/navbarMobileSidebar.d.ts +31 -0
  25. package/lib/contexts/navbarMobileSidebar.d.ts.map +1 -0
  26. package/lib/contexts/navbarMobileSidebar.js +65 -0
  27. package/lib/contexts/navbarMobileSidebar.js.map +1 -0
  28. package/lib/contexts/navbarSecondaryMenu/content.d.ts +37 -0
  29. package/lib/contexts/navbarSecondaryMenu/content.d.ts.map +1 -0
  30. package/lib/contexts/navbarSecondaryMenu/content.js +50 -0
  31. package/lib/contexts/navbarSecondaryMenu/content.js.map +1 -0
  32. package/lib/contexts/navbarSecondaryMenu/display.d.ts +24 -0
  33. package/lib/contexts/navbarSecondaryMenu/display.d.ts.map +1 -0
  34. package/lib/contexts/navbarSecondaryMenu/display.js +62 -0
  35. package/lib/contexts/navbarSecondaryMenu/display.js.map +1 -0
  36. package/lib/hooks/useBackToTopButton.d.ts +27 -0
  37. package/lib/hooks/useBackToTopButton.d.ts.map +1 -0
  38. package/lib/hooks/useBackToTopButton.js +50 -0
  39. package/lib/hooks/useBackToTopButton.js.map +1 -0
  40. package/lib/hooks/useCodeWordWrap.d.ts +15 -0
  41. package/lib/hooks/useCodeWordWrap.d.ts.map +1 -0
  42. package/lib/hooks/useCodeWordWrap.js +67 -0
  43. package/lib/hooks/useCodeWordWrap.js.map +1 -0
  44. package/lib/hooks/useHideableNavbar.d.ts +17 -0
  45. package/lib/hooks/useHideableNavbar.d.ts.map +1 -0
  46. package/lib/hooks/useHideableNavbar.js +65 -0
  47. package/lib/hooks/useHideableNavbar.js.map +1 -0
  48. package/lib/hooks/useLockBodyScroll.d.ts +12 -0
  49. package/lib/hooks/useLockBodyScroll.d.ts.map +1 -0
  50. package/lib/hooks/useLockBodyScroll.js +20 -0
  51. package/lib/hooks/useLockBodyScroll.js.map +1 -0
  52. package/lib/hooks/useMutationObserver.d.ts +4 -0
  53. package/lib/hooks/useMutationObserver.d.ts.map +1 -0
  54. package/lib/hooks/useMutationObserver.js +29 -0
  55. package/lib/hooks/useMutationObserver.js.map +1 -0
  56. package/lib/hooks/usePrismTheme.d.ts +13 -0
  57. package/lib/hooks/usePrismTheme.d.ts.map +1 -0
  58. package/lib/hooks/usePrismTheme.js +21 -0
  59. package/lib/hooks/usePrismTheme.js.map +1 -0
  60. package/lib/hooks/useTOCHighlight.d.ts +25 -0
  61. package/lib/hooks/useTOCHighlight.d.ts.map +1 -0
  62. package/lib/hooks/useTOCHighlight.js +130 -0
  63. package/lib/hooks/useTOCHighlight.js.map +1 -0
  64. package/lib/hooks/useWindowSize.d.ts +27 -0
  65. package/lib/hooks/useWindowSize.d.ts.map +1 -0
  66. package/lib/hooks/useWindowSize.js +49 -0
  67. package/lib/hooks/useWindowSize.js.map +1 -0
  68. package/lib/index.d.ts +30 -0
  69. package/lib/index.d.ts.map +1 -0
  70. package/lib/index.js +37 -0
  71. package/lib/index.js.map +1 -0
  72. package/lib/internal.d.ts +35 -0
  73. package/lib/internal.d.ts.map +1 -0
  74. package/lib/internal.js +45 -0
  75. package/lib/internal.js.map +1 -0
  76. package/lib/translations/blogTranslations.d.ts +20 -0
  77. package/lib/translations/blogTranslations.d.ts.map +1 -0
  78. package/lib/translations/blogTranslations.js +52 -0
  79. package/lib/translations/blogTranslations.js.map +1 -0
  80. package/lib/translations/contentVisibilityTranslations.d.ts +12 -0
  81. package/lib/translations/contentVisibilityTranslations.d.ts.map +1 -0
  82. package/lib/translations/contentVisibilityTranslations.js +30 -0
  83. package/lib/translations/contentVisibilityTranslations.js.map +1 -0
  84. package/lib/translations/docsTranslations.d.ts +8 -0
  85. package/lib/translations/docsTranslations.d.ts.map +1 -0
  86. package/lib/translations/docsTranslations.js +17 -0
  87. package/lib/translations/docsTranslations.js.map +1 -0
  88. package/lib/utils/IntlUtils.d.ts +2 -0
  89. package/lib/utils/IntlUtils.d.ts.map +1 -0
  90. package/lib/utils/IntlUtils.js +20 -0
  91. package/lib/utils/IntlUtils.js.map +1 -0
  92. package/lib/utils/ThemeClassNames.d.ts +99 -0
  93. package/lib/utils/ThemeClassNames.d.ts.map +1 -0
  94. package/lib/utils/ThemeClassNames.js +107 -0
  95. package/lib/utils/ThemeClassNames.js.map +1 -0
  96. package/lib/utils/accessibilityUtils.d.ts +8 -0
  97. package/lib/utils/accessibilityUtils.d.ts.map +1 -0
  98. package/lib/utils/accessibilityUtils.js +10 -0
  99. package/lib/utils/accessibilityUtils.js.map +1 -0
  100. package/lib/utils/admonitionUtils.d.ts +12 -0
  101. package/lib/utils/admonitionUtils.d.ts.map +1 -0
  102. package/lib/utils/admonitionUtils.js +32 -0
  103. package/lib/utils/admonitionUtils.js.map +1 -0
  104. package/lib/utils/anchorUtils.d.ts +16 -0
  105. package/lib/utils/anchorUtils.d.ts.map +1 -0
  106. package/lib/utils/anchorUtils.js +26 -0
  107. package/lib/utils/anchorUtils.js.map +1 -0
  108. package/lib/utils/anchorUtils.module.css +14 -0
  109. package/lib/utils/codeBlockUtils.d.ts +101 -0
  110. package/lib/utils/codeBlockUtils.d.ts.map +1 -0
  111. package/lib/utils/codeBlockUtils.js +310 -0
  112. package/lib/utils/codeBlockUtils.js.map +1 -0
  113. package/lib/utils/emojiUtils.d.ts +20 -0
  114. package/lib/utils/emojiUtils.d.ts.map +1 -0
  115. package/lib/utils/emojiUtils.js +32 -0
  116. package/lib/utils/emojiUtils.js.map +1 -0
  117. package/lib/utils/errorBoundaryUtils.d.ts +31 -0
  118. package/lib/utils/errorBoundaryUtils.d.ts.map +1 -0
  119. package/lib/utils/errorBoundaryUtils.js +48 -0
  120. package/lib/utils/errorBoundaryUtils.js.map +1 -0
  121. package/lib/utils/errorBoundaryUtils.module.css +16 -0
  122. package/lib/utils/footerUtils.d.ts +13 -0
  123. package/lib/utils/footerUtils.d.ts.map +1 -0
  124. package/lib/utils/footerUtils.js +14 -0
  125. package/lib/utils/footerUtils.js.map +1 -0
  126. package/lib/utils/historyUtils.d.ts +39 -0
  127. package/lib/utils/historyUtils.d.ts.map +1 -0
  128. package/lib/utils/historyUtils.js +148 -0
  129. package/lib/utils/historyUtils.js.map +1 -0
  130. package/lib/utils/jsUtils.d.ts +24 -0
  131. package/lib/utils/jsUtils.d.ts.map +1 -0
  132. package/lib/utils/jsUtils.js +43 -0
  133. package/lib/utils/jsUtils.js.map +1 -0
  134. package/lib/utils/metadataUtils.d.ts +38 -0
  135. package/lib/utils/metadataUtils.d.ts.map +1 -0
  136. package/lib/utils/metadataUtils.js +87 -0
  137. package/lib/utils/metadataUtils.js.map +1 -0
  138. package/lib/utils/navbarUtils.d.ts +21 -0
  139. package/lib/utils/navbarUtils.d.ts.map +1 -0
  140. package/lib/utils/navbarUtils.js +35 -0
  141. package/lib/utils/navbarUtils.js.map +1 -0
  142. package/lib/utils/reactUtils.d.ts +60 -0
  143. package/lib/utils/reactUtils.d.ts.map +1 -0
  144. package/lib/utils/reactUtils.js +91 -0
  145. package/lib/utils/reactUtils.js.map +1 -0
  146. package/lib/utils/regexpUtils.d.ts +12 -0
  147. package/lib/utils/regexpUtils.d.ts.map +1 -0
  148. package/lib/utils/regexpUtils.js +18 -0
  149. package/lib/utils/regexpUtils.js.map +1 -0
  150. package/lib/utils/routesUtils.d.ts +26 -0
  151. package/lib/utils/routesUtils.d.ts.map +1 -0
  152. package/lib/utils/routesUtils.js +54 -0
  153. package/lib/utils/routesUtils.js.map +1 -0
  154. package/lib/utils/scrollUtils.d.ts +83 -0
  155. package/lib/utils/scrollUtils.d.ts.map +1 -0
  156. package/lib/utils/scrollUtils.js +201 -0
  157. package/lib/utils/scrollUtils.js.map +1 -0
  158. package/lib/utils/searchUtils.d.ts +8 -0
  159. package/lib/utils/searchUtils.d.ts.map +1 -0
  160. package/lib/utils/searchUtils.js +8 -0
  161. package/lib/utils/searchUtils.js.map +1 -0
  162. package/lib/utils/skipToContentUtils.d.ts +20 -0
  163. package/lib/utils/skipToContentUtils.d.ts.map +1 -0
  164. package/lib/utils/skipToContentUtils.js +75 -0
  165. package/lib/utils/skipToContentUtils.js.map +1 -0
  166. package/lib/utils/storageUtils.d.ts +36 -0
  167. package/lib/utils/storageUtils.d.ts.map +1 -0
  168. package/lib/utils/storageUtils.js +193 -0
  169. package/lib/utils/storageUtils.js.map +1 -0
  170. package/lib/utils/tabsUtils.d.ts +55 -0
  171. package/lib/utils/tabsUtils.d.ts.map +1 -0
  172. package/lib/utils/tabsUtils.js +179 -0
  173. package/lib/utils/tabsUtils.js.map +1 -0
  174. package/lib/utils/tagsUtils.d.ts +20 -0
  175. package/lib/utils/tagsUtils.d.ts.map +1 -0
  176. package/lib/utils/tagsUtils.js +41 -0
  177. package/lib/utils/tagsUtils.js.map +1 -0
  178. package/lib/utils/titleFormatterUtils.d.ts +59 -0
  179. package/lib/utils/titleFormatterUtils.d.ts.map +1 -0
  180. package/lib/utils/titleFormatterUtils.js +49 -0
  181. package/lib/utils/titleFormatterUtils.js.map +1 -0
  182. package/lib/utils/tocUtils.d.ts +36 -0
  183. package/lib/utils/tocUtils.d.ts.map +1 -0
  184. package/lib/utils/tocUtils.js +84 -0
  185. package/lib/utils/tocUtils.js.map +1 -0
  186. package/lib/utils/useAlternatePageUtils.d.ts +32 -0
  187. package/lib/utils/useAlternatePageUtils.d.ts.map +1 -0
  188. package/lib/utils/useAlternatePageUtils.js +42 -0
  189. package/lib/utils/useAlternatePageUtils.js.map +1 -0
  190. package/lib/utils/useLocalPathname.d.ts +13 -0
  191. package/lib/utils/useLocalPathname.d.ts.map +1 -0
  192. package/lib/utils/useLocalPathname.js +19 -0
  193. package/lib/utils/useLocalPathname.js.map +1 -0
  194. package/lib/utils/useLocationChange.d.ts +17 -0
  195. package/lib/utils/useLocationChange.d.ts.map +1 -0
  196. package/lib/utils/useLocationChange.js +31 -0
  197. package/lib/utils/useLocationChange.js.map +1 -0
  198. package/lib/utils/usePluralForm.d.ts +21 -0
  199. package/lib/utils/usePluralForm.d.ts.map +1 -0
  200. package/lib/utils/usePluralForm.js +93 -0
  201. package/lib/utils/usePluralForm.js.map +1 -0
  202. package/lib/utils/useThemeConfig.d.ts +120 -0
  203. package/lib/utils/useThemeConfig.d.ts.map +1 -0
  204. package/lib/utils/useThemeConfig.js +14 -0
  205. package/lib/utils/useThemeConfig.js.map +1 -0
  206. package/package.json +58 -4
  207. package/src/components/Collapsible/index.tsx +250 -0
  208. package/src/components/Details/index.tsx +130 -0
  209. package/src/components/Details/styles.module.css +66 -0
  210. package/src/components/ThemedComponent/index.tsx +75 -0
  211. package/src/components/ThemedComponent/styles.module.css +26 -0
  212. package/src/contexts/announcementBar.tsx +117 -0
  213. package/src/contexts/colorMode.tsx +257 -0
  214. package/src/contexts/navbarMobileSidebar.tsx +119 -0
  215. package/src/contexts/navbarSecondaryMenu/content.tsx +100 -0
  216. package/src/contexts/navbarSecondaryMenu/display.tsx +102 -0
  217. package/src/hooks/useBackToTopButton.ts +73 -0
  218. package/src/hooks/useCodeWordWrap.ts +107 -0
  219. package/src/hooks/useHideableNavbar.ts +81 -0
  220. package/src/hooks/useLockBodyScroll.ts +21 -0
  221. package/src/hooks/useMutationObserver.ts +38 -0
  222. package/src/hooks/usePrismTheme.ts +24 -0
  223. package/src/hooks/useTOCHighlight.ts +189 -0
  224. package/src/hooks/useWindowSize.ts +67 -0
  225. package/src/index.ts +112 -0
  226. package/src/internal.ts +107 -0
  227. package/src/translations/blogTranslations.tsx +89 -0
  228. package/src/translations/contentVisibilityTranslations.tsx +51 -0
  229. package/src/translations/docsTranslations.tsx +28 -0
  230. package/src/types.d.ts +8 -0
  231. package/src/utils/IntlUtils.ts +27 -0
  232. package/src/utils/ThemeClassNames.ts +119 -0
  233. package/src/utils/accessibilityUtils.ts +10 -0
  234. package/src/utils/admonitionUtils.tsx +44 -0
  235. package/src/utils/anchorUtils.module.css +14 -0
  236. package/src/utils/anchorUtils.ts +31 -0
  237. package/src/utils/codeBlockUtils.tsx +494 -0
  238. package/src/utils/emojiUtils.ts +40 -0
  239. package/src/utils/errorBoundaryUtils.module.css +16 -0
  240. package/src/utils/errorBoundaryUtils.tsx +71 -0
  241. package/src/utils/footerUtils.ts +18 -0
  242. package/src/utils/historyUtils.ts +199 -0
  243. package/src/utils/jsUtils.ts +54 -0
  244. package/src/utils/metadataUtils.tsx +145 -0
  245. package/src/utils/navbarUtils.tsx +45 -0
  246. package/src/utils/reactUtils.tsx +120 -0
  247. package/src/utils/regexpUtils.ts +24 -0
  248. package/src/utils/routesUtils.ts +75 -0
  249. package/src/utils/scrollUtils.tsx +303 -0
  250. package/src/utils/searchUtils.ts +8 -0
  251. package/src/utils/skipToContentUtils.tsx +108 -0
  252. package/src/utils/storageUtils.ts +260 -0
  253. package/src/utils/tabsUtils.tsx +307 -0
  254. package/src/utils/tagsUtils.tsx +61 -0
  255. package/src/utils/titleFormatterUtils.tsx +120 -0
  256. package/src/utils/tocUtils.ts +119 -0
  257. package/src/utils/useAlternatePageUtils.ts +80 -0
  258. package/src/utils/useLocalPathname.ts +22 -0
  259. package/src/utils/useLocationChange.ts +41 -0
  260. package/src/utils/usePluralForm.ts +131 -0
  261. package/src/utils/useThemeConfig.ts +147 -0
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@azerion/bunosaurus-theme-common",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "4.0.0",
4
+ "description": "Common code for Docusaurus themes.",
5
+ "main": "./lib/index.js",
6
+ "types": "./lib/index.d.ts",
7
+ "sideEffects": [
8
+ "lib/components/Details/*",
9
+ "*.css"
10
+ ],
11
+ "exports": {
12
+ ".": "./lib/index.js",
13
+ "./internal": "./lib/internal.js",
14
+ "./Details": "./lib/components/Details/index.js"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc && copy-untyped-files",
18
+ "watch": "run-p -c copy:watch build:watch",
19
+ "build:watch": "tsc --watch",
20
+ "copy:watch": "copy-untyped-files --watch",
21
+ "removeThemeInternalReexport": "bun removeThemeInternalReexport.mjs"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/azerion/bunosaurus.git",
29
+ "directory": "packages/docusaurus-theme-common"
30
+ },
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@docusaurus/mdx-loader": "npm:@azerion/bunosaurus-mdx-loader@4.0.0",
34
+ "@docusaurus/module-type-aliases": "npm:@azerion/bunosaurus-module-type-aliases@4.0.0",
35
+ "@docusaurus/utils": "npm:@azerion/bunosaurus-utils@4.0.0",
36
+ "@docusaurus/utils-common": "npm:@azerion/bunosaurus-utils-common@4.0.0",
37
+ "@types/history": "^4.7.11",
38
+ "@types/react": "19.3.0",
39
+ "@types/react-router-config": "^5.0.11",
40
+ "clsx": "^2.1.1",
41
+ "parse-numeric-range": "^1.3.0",
42
+ "prism-react-renderer": "^2.4.1",
43
+ "tslib": "^2.8.1",
44
+ "utility-types": "^3.11.0"
45
+ },
46
+ "devDependencies": {
47
+ "@docusaurus/core": "npm:@azerion/bunosaurus-core@4.0.0",
48
+ "@docusaurus/types": "npm:@azerion/bunosaurus-types@4.0.0",
49
+ "@total-typescript/shoehorn": "^0.1.2",
50
+ "lodash": "^4.18.1",
51
+ "react-router-dom": "^5.3.4"
52
+ },
53
+ "peerDependencies": {
54
+ "react": "^19.3.0",
55
+ "react-dom": "^19.3.0"
56
+ },
57
+ "engines": {
58
+ "bun": ">=1.4.1"
59
+ }
60
+ }
@@ -0,0 +1,250 @@
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 React from 'react';
9
+ import {
10
+ useState,
11
+ useEffect,
12
+ useLayoutEffect,
13
+ useRef,
14
+ useCallback,
15
+ type RefObject,
16
+ type Dispatch,
17
+ type SetStateAction,
18
+ type ReactNode,
19
+ } from 'react';
20
+ import {prefersReducedMotion} from '../../utils/accessibilityUtils';
21
+
22
+ const DefaultAnimationEasing = 'ease-in-out';
23
+
24
+ /**
25
+ * This hook is a very thin wrapper around a `useState`.
26
+ */
27
+ export function useCollapsible({
28
+ initialState,
29
+ }: {
30
+ /** The initial state. Will be non-collapsed by default. */
31
+ initialState?: boolean | (() => boolean);
32
+ }): {
33
+ collapsed: boolean;
34
+ setCollapsed: Dispatch<SetStateAction<boolean>>;
35
+ toggleCollapsed: () => void;
36
+ } {
37
+ const [collapsed, setCollapsed] = useState(initialState ?? false);
38
+
39
+ const toggleCollapsed = useCallback(() => {
40
+ setCollapsed((expanded) => !expanded);
41
+ }, []);
42
+
43
+ return {
44
+ collapsed,
45
+ setCollapsed,
46
+ toggleCollapsed,
47
+ };
48
+ }
49
+
50
+ const CollapsedStyles = {
51
+ display: 'none',
52
+ overflow: 'hidden',
53
+ height: '0px',
54
+ } as const;
55
+
56
+ const ExpandedStyles = {
57
+ display: 'block',
58
+ overflow: 'visible',
59
+ height: 'auto',
60
+ } as const;
61
+
62
+ function applyCollapsedStyle(el: HTMLElement, collapsed: boolean) {
63
+ const collapsedStyles = collapsed ? CollapsedStyles : ExpandedStyles;
64
+ el.style.display = collapsedStyles.display;
65
+ el.style.overflow = collapsedStyles.overflow;
66
+ el.style.height = collapsedStyles.height;
67
+ }
68
+
69
+ /*
70
+ Lex111: Dynamic transition duration is used in Material design, this technique
71
+ is good for a large number of items.
72
+ https://material.io/archive/guidelines/motion/duration-easing.html#duration-easing-dynamic-durations
73
+ https://github.com/mui-org/material-ui/blob/e724d98eba018e55e1a684236a2037e24bcf050c/packages/material-ui/src/styles/createTransitions.js#L40-L43
74
+ */
75
+ function getAutoHeightDuration(height: number) {
76
+ if (prefersReducedMotion()) {
77
+ // Not using 0 because it prevents onTransitionEnd to fire and bubble up :/
78
+ // See https://github.com/facebook/docusaurus/pull/8906
79
+ return 1;
80
+ }
81
+ const constant = height / 36;
82
+ return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);
83
+ }
84
+
85
+ type CollapsibleAnimationConfig = {
86
+ duration?: number;
87
+ easing?: string;
88
+ };
89
+
90
+ function useCollapseAnimation({
91
+ collapsibleRef,
92
+ collapsed,
93
+ animation,
94
+ }: {
95
+ collapsibleRef: RefObject<HTMLElement | null>;
96
+ collapsed: boolean;
97
+ animation?: CollapsibleAnimationConfig;
98
+ }) {
99
+ const mounted = useRef(false);
100
+
101
+ useEffect(() => {
102
+ const el = collapsibleRef.current!;
103
+
104
+ function getTransitionStyles() {
105
+ const height = el.scrollHeight;
106
+ const duration = animation?.duration ?? getAutoHeightDuration(height);
107
+ const easing = animation?.easing ?? DefaultAnimationEasing;
108
+ return {
109
+ transition: `height ${duration}ms ${easing}`,
110
+ height: `${height}px`,
111
+ };
112
+ }
113
+
114
+ function applyTransitionStyles() {
115
+ const transitionStyles = getTransitionStyles();
116
+ el.style.transition = transitionStyles.transition;
117
+ el.style.height = transitionStyles.height;
118
+ }
119
+
120
+ // On mount, we just apply styles, no animated transition
121
+ if (!mounted.current) {
122
+ applyCollapsedStyle(el, collapsed);
123
+ mounted.current = true;
124
+ return undefined;
125
+ }
126
+
127
+ el.style.willChange = 'height';
128
+
129
+ function startAnimation() {
130
+ const animationFrame = requestAnimationFrame(() => {
131
+ // When collapsing
132
+ if (collapsed) {
133
+ applyTransitionStyles();
134
+
135
+ requestAnimationFrame(() => {
136
+ el.style.height = CollapsedStyles.height;
137
+ el.style.overflow = CollapsedStyles.overflow;
138
+ });
139
+ }
140
+ // When expanding
141
+ else {
142
+ el.style.display = 'block';
143
+ requestAnimationFrame(() => {
144
+ applyTransitionStyles();
145
+ });
146
+ }
147
+ });
148
+
149
+ return () => cancelAnimationFrame(animationFrame);
150
+ }
151
+
152
+ return startAnimation();
153
+ }, [collapsibleRef, collapsed, animation]);
154
+ }
155
+
156
+ type CollapsibleElementType = React.ElementType<
157
+ Pick<React.HTMLAttributes<unknown>, 'className' | 'onTransitionEnd' | 'style'>
158
+ >;
159
+
160
+ type CollapsibleBaseProps = {
161
+ /** The actual DOM element to be used in the markup. */
162
+ as?: CollapsibleElementType;
163
+ /** Initial collapsed state. */
164
+ collapsed: boolean;
165
+ children: ReactNode;
166
+ /** Configuration of animation, like `duration` and `easing` */
167
+ animation?: CollapsibleAnimationConfig;
168
+ /**
169
+ * A callback fired when the collapse transition animation ends. Receives
170
+ * the **new** collapsed state: e.g. when
171
+ * expanding, `collapsed` will be `false`. You can use this for some "cleanup"
172
+ * like applying new styles when the container is fully expanded.
173
+ */
174
+ onCollapseTransitionEnd?: (collapsed: boolean) => void;
175
+ /** Class name for the underlying DOM element. */
176
+ className?: string;
177
+ };
178
+
179
+ function CollapsibleBase({
180
+ as: As = 'div',
181
+ collapsed,
182
+ children,
183
+ animation,
184
+ onCollapseTransitionEnd,
185
+ className,
186
+ }: CollapsibleBaseProps) {
187
+ const collapsibleRef = useRef<HTMLElement>(null);
188
+
189
+ useCollapseAnimation({collapsibleRef, collapsed, animation});
190
+
191
+ return (
192
+ <As
193
+ // @ts-expect-error: the "too complicated type" is produced from
194
+ // "CollapsibleElementType" being a huge union
195
+ ref={collapsibleRef as RefObject<never>} // Refs are contravariant, which is not expressible in TS
196
+ onTransitionEnd={(e: React.TransitionEvent) => {
197
+ if (e.propertyName !== 'height') {
198
+ return;
199
+ }
200
+
201
+ applyCollapsedStyle(collapsibleRef.current!, collapsed);
202
+ onCollapseTransitionEnd?.(collapsed);
203
+ }}
204
+ className={className}>
205
+ {children}
206
+ </As>
207
+ );
208
+ }
209
+
210
+ function CollapsibleLazy({collapsed, ...props}: CollapsibleBaseProps) {
211
+ const [mounted, setMounted] = useState(!collapsed);
212
+ // Updated in effect so that first expansion transition can work
213
+ const [lazyCollapsed, setLazyCollapsed] = useState(collapsed);
214
+
215
+ useLayoutEffect(() => {
216
+ if (!collapsed) {
217
+ setMounted(true);
218
+ }
219
+ }, [collapsed]);
220
+
221
+ useLayoutEffect(() => {
222
+ if (mounted) {
223
+ setLazyCollapsed(collapsed);
224
+ }
225
+ }, [mounted, collapsed]);
226
+
227
+ return mounted ? (
228
+ <CollapsibleBase {...props} collapsed={lazyCollapsed} />
229
+ ) : null;
230
+ }
231
+
232
+ type CollapsibleProps = CollapsibleBaseProps & {
233
+ /**
234
+ * Delay rendering of the content till first expansion. Marked as required to
235
+ * force us to think if content should be server-rendered or not. This has
236
+ * perf impact since it reduces html file sizes, but could undermine SEO.
237
+ * @see https://github.com/facebook/docusaurus/issues/4753
238
+ */
239
+ lazy: boolean;
240
+ };
241
+
242
+ /**
243
+ * A headless component providing smooth and uniform collapsing behavior. The
244
+ * component will be invisible (zero height) when collapsed. Doesn't provide
245
+ * interactivity by itself: collapse state is toggled through props.
246
+ */
247
+ export function Collapsible({lazy, ...props}: CollapsibleProps): ReactNode {
248
+ const Comp = lazy ? CollapsibleLazy : CollapsibleBase;
249
+ return <Comp {...props} />;
250
+ }
@@ -0,0 +1,130 @@
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, {
9
+ useRef,
10
+ useState,
11
+ type ComponentProps,
12
+ type ReactElement,
13
+ type ReactNode,
14
+ } from 'react';
15
+ import clsx from 'clsx';
16
+ import useBrokenLinks from '@docusaurus/useBrokenLinks';
17
+ import useIsBrowser from '@docusaurus/useIsBrowser';
18
+ import {useCollapsible, Collapsible} from '../Collapsible';
19
+ import styles from './styles.module.css';
20
+
21
+ function isInSummary(node: HTMLElement | null): boolean {
22
+ if (!node) {
23
+ return false;
24
+ }
25
+ return node.tagName === 'SUMMARY' || isInSummary(node.parentElement);
26
+ }
27
+
28
+ function hasParent(node: HTMLElement | null, parent: HTMLElement): boolean {
29
+ if (!node) {
30
+ return false;
31
+ }
32
+ return node === parent || hasParent(node.parentElement, parent);
33
+ }
34
+
35
+ export type DetailsProps = {
36
+ /**
37
+ * Summary is provided as props, optionally including the wrapping
38
+ * `<summary>` tag
39
+ */
40
+ summary?: ReactElement | string;
41
+ } & ComponentProps<'details'>;
42
+
43
+ /**
44
+ * A mostly un-styled `<details>` element with smooth collapsing. Provides some
45
+ * very lightweight styles, but you should bring your UI.
46
+ */
47
+ export function Details({
48
+ summary,
49
+ children,
50
+ ...props
51
+ }: DetailsProps): ReactNode {
52
+ useBrokenLinks().collectAnchor(props.id);
53
+
54
+ const isBrowser = useIsBrowser();
55
+ const detailsRef = useRef<HTMLDetailsElement>(null);
56
+
57
+ const {collapsed, setCollapsed} = useCollapsible({
58
+ initialState: !props.open,
59
+ });
60
+ // Use a separate state for the actual details prop, because it must be set
61
+ // only after animation completes, otherwise close animations won't work
62
+ const [open, setOpen] = useState(props.open);
63
+
64
+ const summaryElement = React.isValidElement(summary) ? (
65
+ summary
66
+ ) : (
67
+ <summary>{summary ?? 'Details'}</summary>
68
+ );
69
+
70
+ return (
71
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions
72
+ <details
73
+ {...props}
74
+ ref={detailsRef}
75
+ open={open}
76
+ data-collapsed={collapsed}
77
+ className={clsx(
78
+ styles.details,
79
+ isBrowser && styles.isBrowser,
80
+ props.className,
81
+ )}
82
+ onMouseDown={(e) => {
83
+ const target = e.target as HTMLElement;
84
+ // Prevent a double-click to highlight summary text
85
+ if (isInSummary(target) && e.detail > 1) {
86
+ e.preventDefault();
87
+ }
88
+ }}
89
+ onClick={(e) => {
90
+ e.stopPropagation(); // For isolation of multiple nested details/summary
91
+ const target = e.target as HTMLElement;
92
+ const shouldToggle =
93
+ isInSummary(target) && hasParent(target, detailsRef.current!);
94
+ if (!shouldToggle) {
95
+ return;
96
+ }
97
+ e.preventDefault();
98
+ if (collapsed) {
99
+ setCollapsed(false);
100
+ setOpen(true);
101
+ } else {
102
+ setCollapsed(true);
103
+ // Don't do this, it breaks close animation!
104
+ // setOpen(false);
105
+ }
106
+ }}
107
+ onToggle={(e) => {
108
+ // The toggle event also fires when React syncs the open attribute
109
+ // See https://github.com/facebook/docusaurus/pull/12277
110
+ const isOpen = e.currentTarget.open;
111
+ if (isOpen === !collapsed) {
112
+ return;
113
+ }
114
+ setCollapsed(!isOpen);
115
+ setOpen(isOpen);
116
+ }}>
117
+ {summaryElement}
118
+
119
+ <Collapsible
120
+ lazy={false} // Content might matter for SEO in this case
121
+ collapsed={collapsed}
122
+ onCollapseTransitionEnd={(newCollapsed) => {
123
+ setCollapsed(newCollapsed);
124
+ setOpen(!newCollapsed);
125
+ }}>
126
+ <div className={styles.collapsibleContent}>{children}</div>
127
+ </Collapsible>
128
+ </details>
129
+ );
130
+ }
@@ -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
+ /*
9
+ CSS variables, meant to be overridden by final theme
10
+ */
11
+ .details {
12
+ --docusaurus-details-summary-arrow-size: 0.38rem;
13
+ --docusaurus-details-transition: transform 200ms ease;
14
+ --docusaurus-details-decoration-color: grey;
15
+ }
16
+
17
+ .details > summary {
18
+ position: relative;
19
+ cursor: pointer;
20
+ list-style: none;
21
+ padding-left: 1rem;
22
+ }
23
+
24
+ /* TODO: deprecation, need to remove this after Safari will support `::marker` */
25
+ .details > summary::-webkit-details-marker {
26
+ display: none;
27
+ }
28
+
29
+ .details > summary::before {
30
+ position: absolute;
31
+ top: 0.45rem;
32
+ left: 0;
33
+
34
+ /* CSS-only Arrow */
35
+ content: '';
36
+ border-width: var(--docusaurus-details-summary-arrow-size);
37
+ border-style: solid;
38
+ border-color: transparent transparent transparent
39
+ var(--docusaurus-details-decoration-color);
40
+
41
+ /* Arrow rotation anim */
42
+ transform: rotate(0deg);
43
+ transition: var(--docusaurus-details-transition);
44
+ transform-origin: calc(var(--docusaurus-details-summary-arrow-size) / 2) 50%;
45
+ }
46
+
47
+ /* When JS disabled/failed to load: we use the open property for arrow animation: */
48
+ .details[open]:not(.isBrowser) > summary::before,
49
+ /* When JS works: we use the data-attribute for arrow animation */
50
+ .details[data-collapsed='false'].isBrowser > summary::before {
51
+ transform: rotate(90deg);
52
+ }
53
+
54
+ .collapsibleContent {
55
+ margin-top: 1rem;
56
+ border-top: 1px solid var(--docusaurus-details-decoration-color);
57
+ padding-top: 1rem;
58
+ }
59
+
60
+ .collapsibleContent p:last-child {
61
+ margin-bottom: 0;
62
+ }
63
+
64
+ .details > summary > p:last-child {
65
+ margin-bottom: 0;
66
+ }
@@ -0,0 +1,75 @@
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 useIsBrowser from '@docusaurus/useIsBrowser';
11
+ import {useColorMode} from '../../contexts/colorMode';
12
+
13
+ import styles from './styles.module.css';
14
+
15
+ type Theme = 'light' | 'dark';
16
+
17
+ type RenderFn = ({
18
+ theme,
19
+ className,
20
+ }: {
21
+ theme: Theme;
22
+ className: string;
23
+ }) => React.ReactNode;
24
+
25
+ type Props = {
26
+ children: RenderFn;
27
+ className?: string;
28
+ };
29
+
30
+ /**
31
+ * Generic component to render anything themed in light/dark
32
+ * Note: it's preferable to use CSS for theming because this component
33
+ * will need to render all the variants during SSR to avoid a theme flash.
34
+ *
35
+ * Use this only when CSS customizations are not convenient or impossible.
36
+ * For example, rendering themed images or SVGs...
37
+ *
38
+ * @param className applied to all the variants
39
+ * @param children function to render a theme variant
40
+ * @constructor
41
+ */
42
+ export default function ThemedComponent({
43
+ className,
44
+ children,
45
+ }: Props): ReactNode {
46
+ const isBrowser = useIsBrowser();
47
+ const {colorMode} = useColorMode();
48
+
49
+ function getThemesToRender(): Theme[] {
50
+ if (isBrowser) {
51
+ return colorMode === 'dark' ? ['dark'] : ['light'];
52
+ }
53
+ // We need to render both components on the server / hydration to avoid:
54
+ // - a flash of wrong theme before hydration
55
+ // - React hydration mismatches
56
+ // See https://github.com/facebook/docusaurus/pull/3730
57
+ return ['light', 'dark'];
58
+ }
59
+
60
+ return (
61
+ <>
62
+ {getThemesToRender().map((theme) => {
63
+ const themedElement = children({
64
+ theme,
65
+ className: clsx(
66
+ className,
67
+ styles.themedComponent,
68
+ styles[`themedComponent--${theme}`],
69
+ ),
70
+ });
71
+ return <React.Fragment key={theme}>{themedElement}</React.Fragment>;
72
+ })}
73
+ </>
74
+ );
75
+ }
@@ -0,0 +1,26 @@
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
+ .themedComponent {
9
+ display: none;
10
+ }
11
+
12
+ [data-theme='light'] .themedComponent--light {
13
+ display: initial;
14
+ }
15
+
16
+ [data-theme='dark'] .themedComponent--dark {
17
+ display: initial;
18
+ }
19
+
20
+ /*
21
+ JS disabled??? Show light version by default => better than showing nothing
22
+ TODO bad, but we currently always show light mode when there's no data-theme
23
+ */
24
+ html:not([data-theme]) .themedComponent--light {
25
+ display: initial;
26
+ }