@acmekit/docs-ui 2.13.41

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 (236) hide show
  1. package/dist/Items-YPPZD6C6.mjs +312 -0
  2. package/dist/chunk-JD7BP7O5.mjs +13144 -0
  3. package/dist/index.d.mts +1550 -0
  4. package/dist/index.d.ts +1550 -0
  5. package/dist/index.js +15218 -0
  6. package/dist/index.mjs +366 -0
  7. package/package.json +58 -0
  8. package/src/components/Badge/index.tsx +74 -0
  9. package/src/components/BadgesList/index.tsx +18 -0
  10. package/src/components/BetaBadge/index.tsx +24 -0
  11. package/src/components/Bordered/index.tsx +21 -0
  12. package/src/components/BorderedIcon/index.tsx +60 -0
  13. package/src/components/Breadcrumbs/index.tsx +83 -0
  14. package/src/components/Button/index.tsx +100 -0
  15. package/src/components/Card/Layout/Default/index.tsx +124 -0
  16. package/src/components/Card/Layout/Filler/index.tsx +30 -0
  17. package/src/components/Card/Layout/Large/index.tsx +88 -0
  18. package/src/components/Card/Layout/Mini/index.tsx +142 -0
  19. package/src/components/Card/index.tsx +50 -0
  20. package/src/components/CardList/index.tsx +40 -0
  21. package/src/components/ChildDocs/index.tsx +9 -0
  22. package/src/components/CodeBlock/Actions/AskAi/index.tsx +10 -0
  23. package/src/components/CodeBlock/Actions/Copy/index.tsx +59 -0
  24. package/src/components/CodeBlock/Actions/index.tsx +137 -0
  25. package/src/components/CodeBlock/Collapsible/Button/index.tsx +58 -0
  26. package/src/components/CodeBlock/Collapsible/Fade/index.tsx +55 -0
  27. package/src/components/CodeBlock/Collapsible/Lines/index.tsx +22 -0
  28. package/src/components/CodeBlock/Header/Wrapper/index.tsx +46 -0
  29. package/src/components/CodeBlock/Header/index.tsx +67 -0
  30. package/src/components/CodeBlock/Inline/index.tsx +20 -0
  31. package/src/components/CodeBlock/Line/index.tsx +331 -0
  32. package/src/components/CodeBlock/index.tsx +510 -0
  33. package/src/components/CodeMdx/index.tsx +45 -0
  34. package/src/components/CodeTabs/Item/index.tsx +67 -0
  35. package/src/components/CodeTabs/index.tsx +319 -0
  36. package/src/components/ContentMenu/Actions/index.tsx +7 -0
  37. package/src/components/ContentMenu/Products/index.tsx +64 -0
  38. package/src/components/ContentMenu/Toc/index.tsx +148 -0
  39. package/src/components/ContentMenu/Version/index.tsx +77 -0
  40. package/src/components/ContentMenu/index.tsx +31 -0
  41. package/src/components/CopyButton/index.tsx +76 -0
  42. package/src/components/Details/Summary/index.tsx +75 -0
  43. package/src/components/Details/index.tsx +98 -0
  44. package/src/components/DetailsList/index.tsx +32 -0
  45. package/src/components/DottedSeparator/index.tsx +30 -0
  46. package/src/components/EditButton/index.tsx +32 -0
  47. package/src/components/EditDate/index.tsx +33 -0
  48. package/src/components/ErrorPage/Icon/index.tsx +428 -0
  49. package/src/components/ErrorPage/index.tsx +32 -0
  50. package/src/components/Feedback/Solutions/index.tsx +105 -0
  51. package/src/components/Feedback/index.tsx +304 -0
  52. package/src/components/Footer/index.tsx +23 -0
  53. package/src/components/Heading/H1/index.tsx +21 -0
  54. package/src/components/Heading/H2/index.tsx +47 -0
  55. package/src/components/Heading/H3/index.tsx +42 -0
  56. package/src/components/Heading/H4/index.tsx +14 -0
  57. package/src/components/Heading/index.tsx +4 -0
  58. package/src/components/IconHeadline/index.tsx +15 -0
  59. package/src/components/Icons/AiAssistant/index.tsx +462 -0
  60. package/src/components/Icons/ArrowRightDown/index.tsx +30 -0
  61. package/src/components/Icons/BundledProduct/index.tsx +72 -0
  62. package/src/components/Icons/CalendarRefresh/index.tsx +70 -0
  63. package/src/components/Icons/ChefHat/index.tsx +65 -0
  64. package/src/components/Icons/CircleDottedLine/index.tsx +60 -0
  65. package/src/components/Icons/CloudSolid/index.tsx +47 -0
  66. package/src/components/Icons/ColoredAcmeKit/index.tsx +13 -0
  67. package/src/components/Icons/DecisionProcess/index.tsx +58 -0
  68. package/src/components/Icons/Erp/index.tsx +92 -0
  69. package/src/components/Icons/Github/index.tsx +22 -0
  70. package/src/components/Icons/House/index.tsx +23 -0
  71. package/src/components/Icons/ImageBinary/index.tsx +69 -0
  72. package/src/components/Icons/Kapa/index.tsx +22 -0
  73. package/src/components/Icons/Markdown/index.tsx +25 -0
  74. package/src/components/Icons/NavigationDropdown/Admin/index.tsx +33 -0
  75. package/src/components/Icons/NavigationDropdown/Doc/index.tsx +41 -0
  76. package/src/components/Icons/NavigationDropdown/DocV1/index.tsx +37 -0
  77. package/src/components/Icons/NavigationDropdown/Modules/index.tsx +33 -0
  78. package/src/components/Icons/NavigationDropdown/Resources/index.tsx +37 -0
  79. package/src/components/Icons/NavigationDropdown/Store/index.tsx +37 -0
  80. package/src/components/Icons/NavigationDropdown/Ui/index.tsx +37 -0
  81. package/src/components/Icons/NavigationDropdown/User/index.tsx +37 -0
  82. package/src/components/Icons/PuzzleColored/index.tsx +35 -0
  83. package/src/components/Icons/QuestionMark/index.tsx +23 -0
  84. package/src/components/Icons/Restock/index.tsx +55 -0
  85. package/src/components/Icons/ScrollText/index.tsx +68 -0
  86. package/src/components/Icons/ShadedBg/index.tsx +334 -0
  87. package/src/components/Icons/Shop/index.tsx +68 -0
  88. package/src/components/Icons/SidebarLeft/index.tsx +42 -0
  89. package/src/components/Icons/StripeColored/index.tsx +60 -0
  90. package/src/components/Icons/ThumbDown/index.tsx +23 -0
  91. package/src/components/Icons/ThumbUp/index.tsx +23 -0
  92. package/src/components/Icons/WindowPaintbrush/index.tsx +57 -0
  93. package/src/components/Icons/index.tsx +20 -0
  94. package/src/components/InlineCode/index.tsx +42 -0
  95. package/src/components/InlineIcon/index.tsx +21 -0
  96. package/src/components/InlineThemeImage/index.tsx +14 -0
  97. package/src/components/Input/Search/index.tsx +64 -0
  98. package/src/components/Input/Text/index.tsx +39 -0
  99. package/src/components/Kbd/index.tsx +33 -0
  100. package/src/components/Label/index.tsx +19 -0
  101. package/src/components/Link/index.tsx +67 -0
  102. package/src/components/LinkButton/index.tsx +43 -0
  103. package/src/components/Loading/Dots/index.tsx +16 -0
  104. package/src/components/Loading/Spinner/index.tsx +19 -0
  105. package/src/components/Loading/index.tsx +43 -0
  106. package/src/components/MDXComponents/index.tsx +209 -0
  107. package/src/components/MainNav/DesktopMenu/ThemeMenu/index.tsx +76 -0
  108. package/src/components/MainNav/DesktopMenu/index.tsx +100 -0
  109. package/src/components/MainNav/Items/Dropdown/index.tsx +88 -0
  110. package/src/components/MainNav/Items/Link/index.tsx +34 -0
  111. package/src/components/MainNav/Items/index.tsx +61 -0
  112. package/src/components/MainNav/MobileMenu/Main/index.tsx +67 -0
  113. package/src/components/MainNav/MobileMenu/SubMenu/index.tsx +77 -0
  114. package/src/components/MainNav/MobileMenu/index.tsx +103 -0
  115. package/src/components/MainNav/Version/index.tsx +33 -0
  116. package/src/components/MainNav/index.tsx +153 -0
  117. package/src/components/MarkdownContent/index.tsx +41 -0
  118. package/src/components/Menu/Action/index.tsx +43 -0
  119. package/src/components/Menu/Divider/index.tsx +35 -0
  120. package/src/components/Menu/Dropdown/index.tsx +78 -0
  121. package/src/components/Menu/Item/index.tsx +36 -0
  122. package/src/components/Menu/SubMenu/index.tsx +47 -0
  123. package/src/components/Menu/index.tsx +44 -0
  124. package/src/components/Modal/Footer/index.tsx +29 -0
  125. package/src/components/Modal/Header/index.tsx +33 -0
  126. package/src/components/Modal/index.tsx +124 -0
  127. package/src/components/Note/Layout/index.tsx +139 -0
  128. package/src/components/Note/Types/checks.tsx +7 -0
  129. package/src/components/Note/Types/default.tsx +7 -0
  130. package/src/components/Note/Types/error.tsx +7 -0
  131. package/src/components/Note/Types/soon.tsx +7 -0
  132. package/src/components/Note/Types/sucess.tsx +7 -0
  133. package/src/components/Note/Types/warning.tsx +7 -0
  134. package/src/components/Note/index.tsx +32 -0
  135. package/src/components/Notices/DeprecatedNotice/index.tsx +33 -0
  136. package/src/components/Notices/ExpandableNotice/index.tsx +36 -0
  137. package/src/components/Notices/FeatureFlagNotice/index.tsx +36 -0
  138. package/src/components/Notices/VersionNotice/index.tsx +37 -0
  139. package/src/components/Notification/Item/Layout/Default/index.tsx +89 -0
  140. package/src/components/Notification/Item/index.tsx +88 -0
  141. package/src/components/Notification/index.tsx +65 -0
  142. package/src/components/Pagination/Card/index.tsx +80 -0
  143. package/src/components/Pagination/index.tsx +35 -0
  144. package/src/components/Prerequisites/Item/index.tsx +43 -0
  145. package/src/components/Prerequisites/index.tsx +94 -0
  146. package/src/components/RadioItem/index.tsx +38 -0
  147. package/src/components/Rating/index.tsx +133 -0
  148. package/src/components/RootProviders/index.tsx +31 -0
  149. package/src/components/Select/Badge/index.tsx +122 -0
  150. package/src/components/Select/Dropdown/index.tsx +188 -0
  151. package/src/components/Select/Input/index.tsx +123 -0
  152. package/src/components/Select/index.ts +13 -0
  153. package/src/components/Sidebar/Child/index.tsx +43 -0
  154. package/src/components/Sidebar/Item/Category/index.tsx +151 -0
  155. package/src/components/Sidebar/Item/Link/index.tsx +174 -0
  156. package/src/components/Sidebar/Item/Sidebar/index.tsx +67 -0
  157. package/src/components/Sidebar/Item/SubCategory/index.tsx +83 -0
  158. package/src/components/Sidebar/Item/index.tsx +41 -0
  159. package/src/components/Sidebar/Top/MobileClose/index.tsx +21 -0
  160. package/src/components/Sidebar/Top/index.tsx +33 -0
  161. package/src/components/Sidebar/index.tsx +153 -0
  162. package/src/components/SourceCodeLink/index.tsx +37 -0
  163. package/src/components/SplitLists/index.tsx +58 -0
  164. package/src/components/Table/index.tsx +87 -0
  165. package/src/components/Tabs/index.tsx +106 -0
  166. package/src/components/TextArea/index.tsx +30 -0
  167. package/src/components/ThemeImage/index.tsx +26 -0
  168. package/src/components/Toggle/index.tsx +28 -0
  169. package/src/components/Tooltip/index.tsx +65 -0
  170. package/src/components/TypeList/Items/index.tsx +337 -0
  171. package/src/components/TypeList/index.tsx +63 -0
  172. package/src/components/WideSection/index.tsx +25 -0
  173. package/src/components/ZoomImg/index.tsx +17 -0
  174. package/src/components/index.ts +75 -0
  175. package/src/constants.tsx +261 -0
  176. package/src/global-config.ts +11 -0
  177. package/src/hooks/index.ts +14 -0
  178. package/src/hooks/use-active-on-scroll/index.tsx +223 -0
  179. package/src/hooks/use-click-outside/index.tsx +37 -0
  180. package/src/hooks/use-collapsible/index.tsx +128 -0
  181. package/src/hooks/use-collapsible-code-lines/index.tsx +149 -0
  182. package/src/hooks/use-copy/index.tsx +28 -0
  183. package/src/hooks/use-heading-url/index.tsx +32 -0
  184. package/src/hooks/use-is-external-link/index.tsx +19 -0
  185. package/src/hooks/use-keyboard-shortcut/index.tsx +71 -0
  186. package/src/hooks/use-mutation-observer/index.ts +32 -0
  187. package/src/hooks/use-page-scroll-manager/index.tsx +82 -0
  188. package/src/hooks/use-resize-observer/index.ts +20 -0
  189. package/src/hooks/use-scroll-utils/index.tsx +372 -0
  190. package/src/hooks/use-select/index.tsx +99 -0
  191. package/src/hooks/use-tabs/index.tsx +94 -0
  192. package/src/index.ts +8 -0
  193. package/src/layouts/barebone.tsx +18 -0
  194. package/src/layouts/index.ts +4 -0
  195. package/src/layouts/main-content.tsx +86 -0
  196. package/src/layouts/root.tsx +43 -0
  197. package/src/layouts/tight.tsx +29 -0
  198. package/src/layouts/wide.tsx +25 -0
  199. package/src/providers/AiAssistant/index.tsx +65 -0
  200. package/src/providers/BrowserProvider/index.tsx +40 -0
  201. package/src/providers/ColorMode/index.tsx +73 -0
  202. package/src/providers/Layout/index.tsx +52 -0
  203. package/src/providers/MainNav/index.tsx +134 -0
  204. package/src/providers/Mobile/index.tsx +62 -0
  205. package/src/providers/Modal/index.tsx +52 -0
  206. package/src/providers/Notification/index.tsx +149 -0
  207. package/src/providers/Pagination/index.tsx +230 -0
  208. package/src/providers/Search/index.tsx +91 -0
  209. package/src/providers/Sidebar/index.tsx +745 -0
  210. package/src/providers/SiteConfig/index.tsx +70 -0
  211. package/src/providers/index.ts +13 -0
  212. package/src/types/config.ts +34 -0
  213. package/src/types/frontmatter.ts +23 -0
  214. package/src/types/general.ts +1 -0
  215. package/src/types/index.ts +9 -0
  216. package/src/types/menu.ts +40 -0
  217. package/src/types/navigation-dropdown.ts +16 -0
  218. package/src/types/navigation.ts +21 -0
  219. package/src/types/sidebar.ts +109 -0
  220. package/src/types/toc.ts +19 -0
  221. package/src/types/ui.ts +9 -0
  222. package/src/utils/array-same-elms.ts +10 -0
  223. package/src/utils/capitalize.ts +3 -0
  224. package/src/utils/check-sidebar-item-visibility.ts +47 -0
  225. package/src/utils/decode-str.ts +8 -0
  226. package/src/utils/dom-utils.ts +29 -0
  227. package/src/utils/event-parser.ts +54 -0
  228. package/src/utils/get-link-with-base-path.ts +3 -0
  229. package/src/utils/get-navbar-items.ts +55 -0
  230. package/src/utils/get-scrolled-top.ts +8 -0
  231. package/src/utils/index.ts +13 -0
  232. package/src/utils/is-elm-window.ts +3 -0
  233. package/src/utils/is-in-view.ts +10 -0
  234. package/src/utils/os-browser-utils.ts +39 -0
  235. package/src/utils/set-obj-value.ts +38 -0
  236. package/src/utils/sidebar-utils.ts +129 -0
@@ -0,0 +1,319 @@
1
+
2
+ import React, { Children, useCallback, useEffect, useMemo, useRef } from "react"
3
+ import { Badge } from "../../components/Badge"
4
+ import { CodeBlockProps, CodeBlockStyle } from "../../components/CodeBlock"
5
+ import { useColorMode } from "../../providers/ColorMode"
6
+ import clsx from "clsx"
7
+ import { CodeBlockActions, CodeBlockActionsProps } from "../CodeBlock/Actions"
8
+ import { CodeBlockHeaderWrapper } from "../CodeBlock/Header/Wrapper"
9
+ import { BaseTabType, useTabs } from "../../hooks/use-tabs"
10
+
11
+ type CodeTab = BaseTabType & {
12
+ codeProps: CodeBlockProps
13
+ codeBlock: React.ReactNode
14
+ children?: React.ReactNode
15
+ }
16
+
17
+ type CodeTabProps = {
18
+ children: React.ReactNode
19
+ className?: string
20
+ group?: string
21
+ blockStyle?: CodeBlockStyle
22
+ }
23
+
24
+ export const CodeTabs = ({
25
+ children,
26
+ className,
27
+ group = "client",
28
+ blockStyle = "loud",
29
+ }: CodeTabProps) => {
30
+ const { colorMode } = useColorMode()
31
+
32
+ const isCodeBlock = (
33
+ node: React.ReactNode
34
+ ): node is
35
+ | React.ReactElement<unknown, string | React.JSXElementConstructor<any>>
36
+ | React.ReactPortal => {
37
+ if (!React.isValidElement(node)) {
38
+ return false
39
+ }
40
+
41
+ if (node.type === "pre") {
42
+ return true
43
+ }
44
+
45
+ const typedProps = node.props as Record<string, unknown>
46
+
47
+ return "source" in typedProps
48
+ }
49
+
50
+ const getCodeBlockProps = (
51
+ codeBlock: React.ReactElement<
52
+ unknown,
53
+ string | React.JSXElementConstructor<any>
54
+ >
55
+ ): CodeBlockProps | undefined => {
56
+ if (typeof codeBlock.props !== "object" || !codeBlock.props) {
57
+ return undefined
58
+ }
59
+
60
+ if ("source" in codeBlock.props) {
61
+ return codeBlock.props as CodeBlockProps
62
+ }
63
+
64
+ if ("children" in codeBlock.props) {
65
+ if (
66
+ typeof codeBlock.props.children === "object" &&
67
+ codeBlock.props.children
68
+ ) {
69
+ return getCodeBlockProps(
70
+ codeBlock.props.children as React.ReactElement<
71
+ unknown,
72
+ string | React.JSXElementConstructor<any>
73
+ >
74
+ )
75
+ } else if (typeof codeBlock.props.children === "string") {
76
+ const lang = "lang" in codeBlock.props ? codeBlock.props.lang : "ts"
77
+ return {
78
+ ...codeBlock.props,
79
+ source: codeBlock.props.children,
80
+ className:
81
+ "className" in codeBlock.props
82
+ ? codeBlock.props.className
83
+ : `language-${lang}`,
84
+ } as CodeBlockProps
85
+ }
86
+ }
87
+
88
+ return undefined
89
+ }
90
+
91
+ const tabs: CodeTab[] = useMemo(() => {
92
+ const tempTabs: CodeTab[] = []
93
+ Children.forEach(children, (child) => {
94
+ if (!React.isValidElement(child)) {
95
+ return
96
+ }
97
+ const typedChildProps = child.props as CodeTab
98
+ if (
99
+ !typedChildProps.label ||
100
+ !typedChildProps.value ||
101
+ !React.isValidElement(typedChildProps.children)
102
+ ) {
103
+ return
104
+ }
105
+
106
+ const codeBlock: React.ReactNode = isCodeBlock(typedChildProps.children)
107
+ ? typedChildProps.children
108
+ : undefined
109
+
110
+ if (!codeBlock) {
111
+ return
112
+ }
113
+
114
+ let codeBlockProps = codeBlock.props as CodeBlockProps
115
+ const showBadge = !codeBlockProps.title
116
+ const originalBadgeLabel = codeBlockProps.badgeLabel
117
+ const parsedCodeBlockProps = getCodeBlockProps(codeBlock) || {
118
+ source: "",
119
+ }
120
+
121
+ const commonProps = {
122
+ badgeLabel: showBadge ? undefined : originalBadgeLabel,
123
+ hasTabs: true,
124
+ className: clsx("!my-0", parsedCodeBlockProps.className),
125
+ }
126
+
127
+ if (
128
+ typeof codeBlock.type !== "string" &&
129
+ (("name" in codeBlock.type && codeBlock.type.name === "CodeBlock") ||
130
+ "source" in codeBlockProps)
131
+ ) {
132
+ codeBlockProps = {
133
+ ...codeBlockProps,
134
+ ...commonProps,
135
+ }
136
+ }
137
+
138
+ const modifiedProps: CodeBlockProps = {
139
+ ...parsedCodeBlockProps,
140
+ ...commonProps,
141
+ }
142
+
143
+ tempTabs.push({
144
+ label: typedChildProps.label,
145
+ value: typedChildProps.value,
146
+ codeProps: {
147
+ ...modifiedProps,
148
+ badgeLabel: !showBadge ? undefined : originalBadgeLabel,
149
+ },
150
+ codeBlock: {
151
+ ...codeBlock,
152
+ props: {
153
+ ...codeBlockProps,
154
+ children: {
155
+ ...(typeof codeBlockProps.children === "object"
156
+ ? codeBlockProps.children
157
+ : {}),
158
+ props: modifiedProps,
159
+ },
160
+ },
161
+ },
162
+ })
163
+ })
164
+
165
+ return tempTabs
166
+ }, [children])
167
+
168
+ const { selectedTab, changeSelectedTab } = useTabs<CodeTab>({
169
+ tabs,
170
+ group,
171
+ })
172
+
173
+ const tabRefs = useRef<(HTMLButtonElement | null)[]>([])
174
+ const codeTabSelectorRef = useRef<HTMLSpanElement | null>(null)
175
+ const codeTabsWrapperRef = useRef<HTMLDivElement | null>(null)
176
+
177
+ const bgColor = useMemo(
178
+ () =>
179
+ clsx(
180
+ blockStyle === "loud" && "bg-acmekit-contrast-bg-base",
181
+ blockStyle === "subtle" && [
182
+ colorMode === "light" && "bg-acmekit-bg-component",
183
+ colorMode === "dark" && "bg-acmekit-code-bg-header",
184
+ ]
185
+ ),
186
+ [blockStyle, colorMode]
187
+ )
188
+
189
+ const boxShadow = useMemo(
190
+ () =>
191
+ clsx(
192
+ blockStyle === "loud" &&
193
+ "shadow-elevation-code-block dark:shadow-elevation-code-block-dark",
194
+ blockStyle === "subtle" && "shadow-none"
195
+ ),
196
+ [blockStyle]
197
+ )
198
+
199
+ const changeTabSelectorCoordinates = useCallback(
200
+ (selectedTabElm: HTMLElement) => {
201
+ if (!codeTabSelectorRef?.current || !codeTabsWrapperRef?.current) {
202
+ return
203
+ }
204
+ const selectedTabsCoordinates = selectedTabElm.getBoundingClientRect()
205
+ const tabsWrapperCoordinates =
206
+ codeTabsWrapperRef.current.getBoundingClientRect()
207
+ codeTabSelectorRef.current.style.left = `${
208
+ selectedTabsCoordinates.left - tabsWrapperCoordinates.left
209
+ }px`
210
+ codeTabSelectorRef.current.style.width = `${selectedTabsCoordinates.width}px`
211
+ if (blockStyle !== "loud") {
212
+ codeTabSelectorRef.current.style.height = `${selectedTabsCoordinates.height}px`
213
+ }
214
+ },
215
+ [blockStyle]
216
+ )
217
+
218
+ useEffect(() => {
219
+ if (codeTabSelectorRef?.current && tabRefs.current.length) {
220
+ const selectedTabElm = tabRefs.current.find(
221
+ (tab) => tab?.getAttribute("aria-selected") === "true"
222
+ )
223
+ if (selectedTabElm) {
224
+ changeTabSelectorCoordinates(
225
+ selectedTabElm.parentElement || selectedTabElm
226
+ )
227
+ }
228
+ }
229
+ }, [codeTabSelectorRef, tabRefs, changeTabSelectorCoordinates, selectedTab])
230
+
231
+ const actionsProps: CodeBlockActionsProps | undefined = useMemo(() => {
232
+ if (!selectedTab) {
233
+ return
234
+ }
235
+
236
+ return {
237
+ source: selectedTab?.codeProps.source,
238
+ blockStyle,
239
+ noReport: selectedTab?.codeProps.noReport,
240
+ noCopy: selectedTab?.codeProps.noCopy,
241
+ inInnerCode: true,
242
+ showGradientBg: false,
243
+ inHeader: true,
244
+ isCollapsed: false,
245
+ }
246
+ }, [selectedTab])
247
+
248
+ // Reset tabRefs array before each render
249
+ tabRefs.current = []
250
+
251
+ return (
252
+ <div
253
+ className={clsx(
254
+ "my-docs_1 w-full max-w-full",
255
+ "rounded-docs_lg",
256
+ bgColor,
257
+ boxShadow,
258
+ className
259
+ )}
260
+ data-testid="code-tabs"
261
+ >
262
+ <CodeBlockHeaderWrapper blockStyle={blockStyle} ref={codeTabsWrapperRef}>
263
+ <span
264
+ className={clsx(
265
+ "xs:absolute xs:transition-all xs:duration-200 xs:ease-ease xs:bottom-0",
266
+ blockStyle === "loud" && "bg-acmekit-contrast-fg-primary h-px",
267
+ blockStyle === "subtle" && [
268
+ colorMode === "light" &&
269
+ "xs:border-acmekit-border-base xs:bg-acmekit-bg-base",
270
+ colorMode === "dark" &&
271
+ "xs:border-acmekit-code-border xs:bg-acmekit-code-bg-base",
272
+ ]
273
+ )}
274
+ ref={codeTabSelectorRef}
275
+ ></span>
276
+ <div className="flex gap-docs_1 items-center">
277
+ {selectedTab?.codeProps.badgeLabel && (
278
+ <Badge
279
+ variant={selectedTab?.codeProps.badgeColor || "code"}
280
+ className="!font-base"
281
+ >
282
+ {selectedTab.codeProps.badgeLabel}
283
+ </Badge>
284
+ )}
285
+ <ul
286
+ className={clsx(
287
+ "!list-none flex gap-docs_0.75 items-center",
288
+ "p-0 mb-0"
289
+ )}
290
+ >
291
+ {Children.map(children, (child, index) => {
292
+ if (!React.isValidElement(child)) {
293
+ return <></>
294
+ }
295
+
296
+ return (
297
+ <child.type
298
+ {...(typeof child.props === "object" ? child.props : {})}
299
+ changeSelectedTab={changeSelectedTab}
300
+ pushRef={(tabButton: HTMLButtonElement | null) =>
301
+ tabRefs.current.push(tabButton)
302
+ }
303
+ blockStyle={blockStyle}
304
+ isSelected={
305
+ !selectedTab
306
+ ? index === 0
307
+ : selectedTab.value === (child.props as CodeTab).value
308
+ }
309
+ />
310
+ )
311
+ })}
312
+ </ul>
313
+ </div>
314
+ {actionsProps && <CodeBlockActions {...actionsProps} />}
315
+ </CodeBlockHeaderWrapper>
316
+ {selectedTab?.codeBlock}
317
+ </div>
318
+ )
319
+ }
@@ -0,0 +1,7 @@
1
+ import React from "react"
2
+
3
+ export const ContentMenuActions = () => {
4
+ // "View as Markdown" requires server-side .html.md handler
5
+ // which is not yet implemented. Hidden until supported.
6
+ return null
7
+ }
@@ -0,0 +1,64 @@
1
+
2
+ import React, { useMemo } from "react"
3
+ import { useSiteConfig } from "../../../providers/SiteConfig"
4
+ import { products } from "../../../constants"
5
+ import { Product } from "../../../types"
6
+ import { BorderedIcon } from "../../BorderedIcon"
7
+ import clsx from "clsx"
8
+
9
+ export const ContentMenuProducts = () => {
10
+ const { frontmatter, config } = useSiteConfig()
11
+
12
+ const loadedProducts = useMemo(() => {
13
+ return frontmatter.products
14
+ ?.sort()
15
+ .map((product) => {
16
+ return products.find(
17
+ (p) => p.name.toLowerCase() === product.toLowerCase()
18
+ )
19
+ })
20
+ .filter(Boolean) as Product[]
21
+ }, [frontmatter.products])
22
+
23
+ if (!loadedProducts?.length) {
24
+ return null
25
+ }
26
+
27
+ const getProductUrl = (product: Product) => {
28
+ return `${config.baseUrl}${product.path}`
29
+ }
30
+
31
+ const getProductImageUrl = (product: Product) => {
32
+ return `${config.basePath}${product.image}`
33
+ }
34
+
35
+ return (
36
+ <div className="flex flex-col gap-docs_0.5">
37
+ <span className="text-x-small-plus text-acmekit-fg-muted">
38
+ Modules used
39
+ </span>
40
+ {loadedProducts.map((product, index) => (
41
+ <a
42
+ key={index}
43
+ href={getProductUrl(product)}
44
+ className="flex gap-docs_0.5 items-center group"
45
+ data-testid="product-link"
46
+ >
47
+ <BorderedIcon
48
+ wrapperClassName={clsx("bg-acmekit-bg-base")}
49
+ icon={getProductImageUrl(product)}
50
+ iconWidth={16}
51
+ iconHeight={16}
52
+ />
53
+ <span
54
+ className={
55
+ "text-acmekit-fg-subtle text-x-small-plus group-hover:text-acmekit-fg-base transition-colors"
56
+ }
57
+ >
58
+ {product.title}
59
+ </span>
60
+ </a>
61
+ ))}
62
+ </div>
63
+ )
64
+ }
@@ -0,0 +1,148 @@
1
+
2
+ import React, { useEffect } from "react"
3
+ import { ToCItem, ToCItemUi } from "../../../types"
4
+ import { useScrollController } from "../../../hooks/use-scroll-utils"
5
+ import {
6
+ ActiveOnScrollItem,
7
+ useActiveOnScroll,
8
+ } from "../../../hooks/use-active-on-scroll"
9
+ import clsx from "clsx"
10
+ import { useSiteConfig } from "../../../providers/SiteConfig"
11
+ import { Loading } from "../../Loading"
12
+
13
+ export const ContentMenuToc = () => {
14
+ const { toc: items, frontmatter, setToc } = useSiteConfig()
15
+ const { items: generatedItems, activeItemId } = useActiveOnScroll({
16
+ maxLevel: 4,
17
+ })
18
+
19
+ const formatHeadingContent = (heading: HTMLHeadingElement): string => {
20
+ // Use textContent to get all text regardless of nesting
21
+ // (rehype-autolink-headings wraps text in <a> elements)
22
+ // Strip trailing "#" from CopyButton anchor rendered by H2/H3
23
+ return (heading.textContent || "").replace(/#$/, "").trim()
24
+ }
25
+
26
+ const formatHeadingObject = ({
27
+ heading,
28
+ children,
29
+ }: ActiveOnScrollItem): ToCItemUi => {
30
+ const level = parseInt(heading.tagName.replace("H", ""))
31
+ return {
32
+ title: formatHeadingContent(heading),
33
+ id: heading.id,
34
+ level,
35
+ children: children?.map(formatHeadingObject),
36
+ associatedHeading: heading as HTMLHeadingElement,
37
+ }
38
+ }
39
+
40
+ useEffect(() => {
41
+ if (
42
+ frontmatter.generate_toc &&
43
+ generatedItems &&
44
+ items?.length !== generatedItems.length
45
+ ) {
46
+ const tocItems: ToCItem[] = generatedItems.map(formatHeadingObject)
47
+ setToc(tocItems)
48
+ }
49
+ }, [frontmatter, generatedItems])
50
+
51
+ useEffect(() => {
52
+ const activeElement = document.querySelector(
53
+ ".toc-item a[href='#" + activeItemId + "']"
54
+ ) as HTMLAnchorElement
55
+ if (!activeElement) {
56
+ return
57
+ }
58
+
59
+ activeElement.scrollIntoView({
60
+ behavior: "smooth",
61
+ block: "center",
62
+ inline: "nearest",
63
+ })
64
+ }, [activeItemId])
65
+
66
+ if (items && !items.length) {
67
+ return <></>
68
+ }
69
+
70
+ return (
71
+ <div className="h-max max-h-full overflow-y-hidden flex relative flex-col">
72
+ <div className="absolute left-0 top-docs_0.5 h-[calc(100%-8px)] w-[1.5px] bg-acmekit-border-base" />
73
+ {items !== null && (
74
+ <TocList
75
+ items={items}
76
+ activeItemId={activeItemId}
77
+ className="relative overflow-y-auto"
78
+ />
79
+ )}
80
+ {items === null && <EmptyTocItems />}
81
+ </div>
82
+ )
83
+ }
84
+
85
+ type TocListProps = {
86
+ items: ToCItem[]
87
+ activeItemId: string
88
+ className?: string
89
+ }
90
+
91
+ const TocList = ({ items, activeItemId, className }: TocListProps) => {
92
+ return (
93
+ <ul className={className} data-testid="toc-list">
94
+ {items.map((item) => (
95
+ <TocItem item={item} key={item.id} activeItemId={activeItemId} />
96
+ ))}
97
+ </ul>
98
+ )
99
+ }
100
+
101
+ type TocItemProps = {
102
+ item: ToCItem
103
+ activeItemId: string
104
+ }
105
+
106
+ const TocItem = ({ item, activeItemId }: TocItemProps) => {
107
+ const { scrollToElement } = useScrollController()
108
+ return (
109
+ <li className="w-full pt-docs_0.5 toc-item" data-testid="toc-item">
110
+ <a
111
+ href={`#${item.id}`}
112
+ className={clsx(
113
+ "text-x-small-plus block w-full relative",
114
+ item.id !== activeItemId &&
115
+ "text-acmekit-fg-muted hover:text-acmekit-fg-base border-transparent"
116
+ )}
117
+ style={{
118
+ paddingLeft: `${(item.level - 1) * 12}px`,
119
+ }}
120
+ onClick={(e) => {
121
+ e.preventDefault()
122
+ history.pushState({}, "", `#${item.id}`)
123
+ const elm = document.getElementById(item.id) as HTMLElement
124
+ scrollToElement(elm)
125
+ }}
126
+ >
127
+ <span
128
+ className={clsx(
129
+ "absolute left-0 top-0 w-[1.5px] h-full bg-acmekit-fg-base rounded-full",
130
+ item.id !== activeItemId && "invisible"
131
+ )}
132
+ />
133
+ {item.title}
134
+ </a>
135
+ {(item.children?.length ?? 0) > 0 && (
136
+ <TocList items={item.children!} activeItemId={activeItemId} />
137
+ )}
138
+ </li>
139
+ )
140
+ }
141
+
142
+ const EmptyTocItems = () => {
143
+ return (
144
+ <div className="animate-pulse" data-testid="empty-toc-items">
145
+ <Loading count={5} className="pt-docs_0.5 px-docs_0.75 !my-0" />
146
+ </div>
147
+ )
148
+ }
@@ -0,0 +1,77 @@
1
+ import React, { useEffect, useState } from "react"
2
+ import { Card } from "../../Card"
3
+ import { useIsBrowser } from "../../../providers/BrowserProvider"
4
+ import { useSiteConfig } from "../../../providers/SiteConfig"
5
+ import clsx from "clsx"
6
+
7
+ export const LOCAL_STORAGE_KEY = "last-version"
8
+
9
+ export const ContentMenuVersion = () => {
10
+ const {
11
+ config: { version },
12
+ } = useSiteConfig()
13
+ const [showNewVersion, setShowNewVersion] = useState(false)
14
+ const { isBrowser } = useIsBrowser()
15
+ const cardRef = React.useRef<HTMLDivElement>(null)
16
+
17
+ useEffect(() => {
18
+ if (!isBrowser || !version) {
19
+ return
20
+ }
21
+
22
+ const storedVersion = localStorage.getItem(LOCAL_STORAGE_KEY)
23
+ if (storedVersion !== version.number) {
24
+ setShowNewVersion(true)
25
+ }
26
+ }, [isBrowser, version])
27
+
28
+ const handleClose = () => {
29
+ if (!showNewVersion || !version) {
30
+ return
31
+ }
32
+
33
+ setShowNewVersion(false)
34
+ localStorage.setItem(LOCAL_STORAGE_KEY, version.number)
35
+ }
36
+
37
+ useEffect(() => {
38
+ if (!showNewVersion || !version || version.hide || !cardRef.current) {
39
+ return
40
+ }
41
+
42
+ cardRef.current.classList.add("animate", "animate-fadeInDown")
43
+ }, [showNewVersion, version, cardRef])
44
+
45
+ if (!version || version.hide) {
46
+ return null
47
+ }
48
+
49
+ return (
50
+ <Card
51
+ type="mini"
52
+ title={`New version`}
53
+ text={`v${version.number} details`}
54
+ closeable
55
+ onClose={handleClose}
56
+ href={version.releaseUrl}
57
+ hrefProps={{
58
+ target: "_blank",
59
+ rel: "noopener noreferrer",
60
+ }}
61
+ themeImage={version.bannerImage}
62
+ imageDimensions={{
63
+ width: 64,
64
+ height: 40,
65
+ }}
66
+ className={clsx(
67
+ "!border-0 !bg-acmekit-bg-component hover:!bg-acmekit-bg-component-hover",
68
+ "hover:!bg-acmekit-bg-component-hover animation-fill-forwards",
69
+ "opacity-0"
70
+ )}
71
+ iconClassName={clsx(
72
+ "!shadow-none border-[0.5px] border-acmekit-alphas-alpha-250"
73
+ )}
74
+ cardRef={cardRef}
75
+ />
76
+ )
77
+ }
@@ -0,0 +1,31 @@
1
+
2
+ import clsx from "clsx"
3
+ import React from "react"
4
+ import { ContentMenuVersion } from "./Version"
5
+ import { ContentMenuToc } from "./Toc"
6
+ import { ContentMenuActions } from "./Actions"
7
+ import { ContentMenuProducts } from "./Products"
8
+ import { useLayout } from "../../providers/Layout"
9
+
10
+ export const ContentMenu = () => {
11
+ const { showCollapsedNavbar } = useLayout()
12
+
13
+ return (
14
+ <div
15
+ className={clsx(
16
+ "hidden lg:flex w-full max-w-sidebar-lg",
17
+ "flex-col gap-docs_2 pb-docs_1.5 mr-docs_1",
18
+ "fixed top-[57px] right-docs_0.25 z-10",
19
+ showCollapsedNavbar && "max-h-[calc(100%-112px)] pt-[84px]",
20
+ !showCollapsedNavbar && "max-h-[calc(100%-56px)] pt-[28px]"
21
+ )}
22
+ >
23
+ <ContentMenuVersion />
24
+ <div className="flex flex-col gap-docs_1.5 flex-1 overflow-auto">
25
+ <ContentMenuToc />
26
+ <ContentMenuActions />
27
+ <ContentMenuProducts />
28
+ </div>
29
+ </div>
30
+ )
31
+ }