zudoku 0.3.0-dev.82 → 0.3.0-dev.84

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 (192) hide show
  1. package/cli.js +5 -1
  2. package/dist/app/demo.js +5 -4
  3. package/dist/app/demo.js.map +1 -1
  4. package/dist/app/main.js +3 -1
  5. package/dist/app/main.js.map +1 -1
  6. package/dist/app/standalone.js +5 -4
  7. package/dist/app/standalone.js.map +1 -1
  8. package/dist/config/validators/ResolvedSidebarSchema.d.ts +18 -0
  9. package/dist/config/validators/ResolvedSidebarSchema.js +76 -0
  10. package/dist/config/validators/ResolvedSidebarSchema.js.map +1 -0
  11. package/dist/config/validators/SidebarSchema.d.ts +177 -0
  12. package/dist/config/validators/SidebarSchema.js +71 -0
  13. package/dist/config/validators/SidebarSchema.js.map +1 -0
  14. package/dist/config/validators/validate.d.ts +411 -59
  15. package/dist/config/validators/validate.js +22 -4
  16. package/dist/config/validators/validate.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/lib/components/DevPortal.js +1 -1
  19. package/dist/lib/components/DevPortal.js.map +1 -1
  20. package/dist/lib/components/Header.js.map +1 -1
  21. package/dist/lib/components/Heading.d.ts +1 -1
  22. package/dist/lib/components/Layout.js +2 -2
  23. package/dist/lib/components/Layout.js.map +1 -1
  24. package/dist/lib/components/TopNavigation.js +5 -5
  25. package/dist/lib/components/TopNavigation.js.map +1 -1
  26. package/dist/lib/components/context/DevPortalProvider.d.ts +9 -3
  27. package/dist/lib/components/context/DevPortalProvider.js +11 -23
  28. package/dist/lib/components/context/DevPortalProvider.js.map +1 -1
  29. package/dist/lib/components/context/ThemeContext.d.ts +1 -4
  30. package/dist/lib/components/context/ThemeContext.js +3 -29
  31. package/dist/lib/components/context/ThemeContext.js.map +1 -1
  32. package/dist/lib/components/context/ThemeProvider.d.ts +4 -0
  33. package/dist/lib/components/context/ThemeProvider.js +23 -0
  34. package/dist/lib/components/context/ThemeProvider.js.map +1 -0
  35. package/dist/lib/components/navigation/Sidebar.d.ts +1 -0
  36. package/dist/lib/components/navigation/Sidebar.js +12 -0
  37. package/dist/lib/components/navigation/Sidebar.js.map +1 -0
  38. package/dist/lib/components/navigation/SidebarBadge.d.ts +22 -0
  39. package/dist/lib/components/navigation/SidebarBadge.js +24 -0
  40. package/dist/lib/components/navigation/SidebarBadge.js.map +1 -0
  41. package/dist/lib/components/navigation/SidebarCategory.d.ts +5 -0
  42. package/dist/lib/components/navigation/SidebarCategory.js +33 -0
  43. package/dist/lib/components/navigation/SidebarCategory.js.map +1 -0
  44. package/dist/lib/components/navigation/SidebarItem.d.ts +12 -0
  45. package/dist/lib/components/navigation/SidebarItem.js +42 -0
  46. package/dist/lib/components/navigation/SidebarItem.js.map +1 -0
  47. package/dist/lib/components/navigation/{SideNavigationWrapper.d.ts → SidebarWrapper.d.ts} +1 -1
  48. package/dist/lib/components/navigation/{SideNavigationWrapper.js → SidebarWrapper.js} +2 -2
  49. package/dist/lib/components/navigation/SidebarWrapper.js.map +1 -0
  50. package/dist/lib/components/navigation/utils.d.ts +16 -0
  51. package/dist/lib/components/navigation/utils.js +85 -0
  52. package/dist/lib/components/navigation/utils.js.map +1 -0
  53. package/dist/lib/core/DevPortalContext.d.ts +9 -32
  54. package/dist/lib/core/DevPortalContext.js +8 -5
  55. package/dist/lib/core/DevPortalContext.js.map +1 -1
  56. package/dist/lib/core/plugins.d.ts +6 -8
  57. package/dist/lib/core/plugins.js.map +1 -1
  58. package/dist/lib/plugins/api-keys/SettingsApiKeys.js +16 -2
  59. package/dist/lib/plugins/api-keys/SettingsApiKeys.js.map +1 -1
  60. package/dist/lib/plugins/api-keys/index.js +6 -0
  61. package/dist/lib/plugins/api-keys/index.js.map +1 -1
  62. package/dist/lib/plugins/markdown/MdxPage.js +5 -36
  63. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  64. package/dist/lib/plugins/markdown/generateRoutes.js +20 -43
  65. package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -1
  66. package/dist/lib/plugins/openapi/Sidecar.js +12 -2
  67. package/dist/lib/plugins/openapi/Sidecar.js.map +1 -1
  68. package/dist/lib/plugins/openapi/index.js +14 -11
  69. package/dist/lib/plugins/openapi/index.js.map +1 -1
  70. package/dist/lib/plugins/openapi/interfaces.d.ts +1 -1
  71. package/dist/lib/util/useScrollToAnchor.js +31 -17
  72. package/dist/lib/util/useScrollToAnchor.js.map +1 -1
  73. package/dist/vite/plugin-sidebar.d.ts +3 -0
  74. package/dist/vite/plugin-sidebar.js +23 -0
  75. package/dist/vite/plugin-sidebar.js.map +1 -0
  76. package/dist/vite/plugin.js +2 -0
  77. package/dist/vite/plugin.js.map +1 -1
  78. package/lib/{AuthenticationPlugin-XS0DoAhE.js → AuthenticationPlugin-DgwV0hVu.js} +7 -7
  79. package/lib/{AuthenticationPlugin-XS0DoAhE.js.map → AuthenticationPlugin-DgwV0hVu.js.map} +1 -1
  80. package/lib/{CategoryHeading-DCmchnA1.js → CategoryHeading-BWq12Bfa.js} +3 -3
  81. package/lib/{CategoryHeading-DCmchnA1.js.map → CategoryHeading-BWq12Bfa.js.map} +1 -1
  82. package/lib/{Combination-C442XfGG.js → Combination-DkycFHkm.js} +4 -4
  83. package/lib/{Combination-C442XfGG.js.map → Combination-DkycFHkm.js.map} +1 -1
  84. package/lib/{DevPortalProvider-BWeAysxF.js → DevPortalProvider-CTxoCHIT.js} +375 -417
  85. package/lib/DevPortalProvider-CTxoCHIT.js.map +1 -0
  86. package/lib/DeveloperHint-BQSFXH01.js +10 -0
  87. package/lib/{DeveloperHint-DQVwIery.js.map → DeveloperHint-BQSFXH01.js.map} +1 -1
  88. package/lib/{Input-3IEt27jb.js → Input-BclXSY0g.js} +5 -5
  89. package/lib/{Input-3IEt27jb.js.map → Input-BclXSY0g.js.map} +1 -1
  90. package/lib/{Markdown-QsZ-PHET.js → Markdown-B_Gax7at.js} +1136 -1152
  91. package/lib/{Markdown-QsZ-PHET.js.map → Markdown-B_Gax7at.js.map} +1 -1
  92. package/lib/{MdxPage-CA1WmW14.js → MdxPage-Crlr0GmN.js} +67 -81
  93. package/lib/MdxPage-Crlr0GmN.js.map +1 -0
  94. package/lib/{OperationList-CHK_erYP.js → OperationList-nQ0bd8TU.js} +8 -8
  95. package/lib/{OperationList-CHK_erYP.js.map → OperationList-nQ0bd8TU.js.map} +1 -1
  96. package/lib/Route-CNvxEBnR.js +14 -0
  97. package/lib/{Route-D70pGn9n.js.map → Route-CNvxEBnR.js.map} +1 -1
  98. package/lib/{SlotletProvider-B71hNEUL.js → SlotletProvider-CzMAO73_.js} +12 -12
  99. package/lib/{SlotletProvider-B71hNEUL.js.map → SlotletProvider-CzMAO73_.js.map} +1 -1
  100. package/lib/Spinner-fF-Xv-gw.js +274 -0
  101. package/lib/Spinner-fF-Xv-gw.js.map +1 -0
  102. package/lib/index-7kcHaXD6.js +1771 -0
  103. package/lib/index-7kcHaXD6.js.map +1 -0
  104. package/lib/{index-Bl6YeerK.js → index-CgCPw6Jn.js} +1026 -1043
  105. package/lib/index-CgCPw6Jn.js.map +1 -0
  106. package/lib/{index-BH-Ub36F.js → index-DkuZvRNP.js} +4 -4
  107. package/lib/{index-BH-Ub36F.js.map → index-DkuZvRNP.js.map} +1 -1
  108. package/lib/joinPath-VeNuJa7y.js +8 -0
  109. package/lib/joinPath-VeNuJa7y.js.map +1 -0
  110. package/lib/jsx-runtime-B6kdoens.js +635 -0
  111. package/lib/jsx-runtime-B6kdoens.js.map +1 -0
  112. package/lib/{AnchorLink-BZcpTwOs.js → utils-CzT_9Tsn.js} +258 -214
  113. package/lib/utils-CzT_9Tsn.js.map +1 -0
  114. package/lib/zudoku.auth-clerk.js +1 -1
  115. package/lib/zudoku.auth-openid.js +2 -2
  116. package/lib/zudoku.components.js +1229 -1227
  117. package/lib/zudoku.components.js.map +1 -1
  118. package/lib/zudoku.plugin-api-keys.js +129 -107
  119. package/lib/zudoku.plugin-api-keys.js.map +1 -1
  120. package/lib/zudoku.plugin-custom-page.js +2 -2
  121. package/lib/zudoku.plugin-markdown.js +21 -38
  122. package/lib/zudoku.plugin-markdown.js.map +1 -1
  123. package/lib/zudoku.plugin-openapi.js +8 -6
  124. package/lib/zudoku.plugin-openapi.js.map +1 -1
  125. package/package.json +4 -1
  126. package/src/app/demo.tsx +5 -4
  127. package/src/app/main.css +2 -2
  128. package/src/app/main.tsx +3 -1
  129. package/src/app/standalone.tsx +5 -4
  130. package/src/lib/components/DevPortal.tsx +1 -1
  131. package/src/lib/components/Header.tsx +2 -2
  132. package/src/lib/components/Layout.tsx +2 -2
  133. package/src/lib/components/TopNavigation.tsx +5 -5
  134. package/src/lib/components/context/DevPortalProvider.ts +11 -28
  135. package/src/lib/components/context/ThemeContext.tsx +3 -41
  136. package/src/lib/components/context/ThemeProvider.tsx +27 -0
  137. package/src/lib/components/navigation/{SideNavigation.tsx → Sidebar.tsx} +7 -7
  138. package/src/lib/components/navigation/SidebarBadge.tsx +40 -0
  139. package/src/lib/components/navigation/SidebarCategory.tsx +105 -0
  140. package/src/lib/components/navigation/SidebarItem.tsx +96 -0
  141. package/src/lib/components/navigation/{SideNavigationWrapper.tsx → SidebarWrapper.tsx} +1 -1
  142. package/src/lib/components/navigation/utils.ts +120 -0
  143. package/src/lib/core/DevPortalContext.ts +12 -44
  144. package/src/lib/core/plugins.ts +6 -13
  145. package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +31 -10
  146. package/src/lib/plugins/api-keys/index.tsx +10 -0
  147. package/src/lib/plugins/markdown/MdxPage.tsx +14 -50
  148. package/src/lib/plugins/markdown/generateRoutes.tsx +29 -57
  149. package/src/lib/plugins/openapi/Sidecar.tsx +15 -2
  150. package/src/lib/plugins/openapi/index.tsx +17 -23
  151. package/src/lib/plugins/openapi/interfaces.ts +1 -1
  152. package/src/lib/util/useScrollToAnchor.ts +39 -18
  153. package/dist/lib/components/navigation/SideNavigation.d.ts +0 -1
  154. package/dist/lib/components/navigation/SideNavigation.js +0 -12
  155. package/dist/lib/components/navigation/SideNavigation.js.map +0 -1
  156. package/dist/lib/components/navigation/SideNavigationCategory.d.ts +0 -4
  157. package/dist/lib/components/navigation/SideNavigationCategory.js +0 -26
  158. package/dist/lib/components/navigation/SideNavigationCategory.js.map +0 -1
  159. package/dist/lib/components/navigation/SideNavigationItem.d.ts +0 -9
  160. package/dist/lib/components/navigation/SideNavigationItem.js +0 -44
  161. package/dist/lib/components/navigation/SideNavigationItem.js.map +0 -1
  162. package/dist/lib/components/navigation/SideNavigationWrapper.js.map +0 -1
  163. package/dist/lib/components/navigation/useNavigationCollapsibleState.d.ts +0 -9
  164. package/dist/lib/components/navigation/useNavigationCollapsibleState.js +0 -28
  165. package/dist/lib/components/navigation/useNavigationCollapsibleState.js.map +0 -1
  166. package/dist/lib/components/navigation/util.d.ts +0 -8
  167. package/dist/lib/components/navigation/util.js +0 -15
  168. package/dist/lib/components/navigation/util.js.map +0 -1
  169. package/dist/lib/plugins/openapi/MethodBadge.d.ts +0 -13
  170. package/dist/lib/plugins/openapi/MethodBadge.js +0 -26
  171. package/dist/lib/plugins/openapi/MethodBadge.js.map +0 -1
  172. package/dist/lib/util/traverseNavigation.d.ts +0 -6
  173. package/dist/lib/util/traverseNavigation.js +0 -30
  174. package/dist/lib/util/traverseNavigation.js.map +0 -1
  175. package/lib/AnchorLink-BZcpTwOs.js.map +0 -1
  176. package/lib/DevPortalProvider-BWeAysxF.js.map +0 -1
  177. package/lib/DeveloperHint-DQVwIery.js +0 -10
  178. package/lib/MdxPage-CA1WmW14.js.map +0 -1
  179. package/lib/Route-D70pGn9n.js +0 -13
  180. package/lib/Spinner-Coi7ORUV.js +0 -244
  181. package/lib/Spinner-Coi7ORUV.js.map +0 -1
  182. package/lib/index-Bl6YeerK.js.map +0 -1
  183. package/lib/index-Dt-pU7Vu.js +0 -916
  184. package/lib/index-Dt-pU7Vu.js.map +0 -1
  185. package/lib/jsx-runtime-CJBdjYYx.js +0 -1526
  186. package/lib/jsx-runtime-CJBdjYYx.js.map +0 -1
  187. package/src/lib/components/navigation/SideNavigationCategory.tsx +0 -72
  188. package/src/lib/components/navigation/SideNavigationItem.tsx +0 -148
  189. package/src/lib/components/navigation/useNavigationCollapsibleState.ts +0 -42
  190. package/src/lib/components/navigation/util.ts +0 -38
  191. package/src/lib/plugins/openapi/MethodBadge.tsx +0 -36
  192. package/src/lib/util/traverseNavigation.ts +0 -55
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-markdown.js","sources":["../src/lib/plugins/markdown/generateRoutes.tsx","../src/lib/plugins/markdown/index.tsx"],"sourcesContent":["import { Navigate, type RouteObject } from \"react-router-dom\";\nimport { useTopNavigationItem } from \"../../components/context/DevPortalProvider.js\";\nimport { isPathItem } from \"../../components/navigation/util.js\";\nimport { traverseNavigation } from \"../../util/traverseNavigation.js\";\n\nimport {\n MarkdownPluginDefaultOptions,\n MarkdownPluginOptions,\n} from \"./index.js\";\n\nexport const generateRoutes = (\n markdownFiles: MarkdownPluginOptions[\"markdownFiles\"],\n defaultOptions?: MarkdownPluginDefaultOptions,\n): RouteObject[] => {\n const routes = Object.entries(markdownFiles).flatMap(\n ([file, importPromise]) => {\n // @todo we can pass in the folder name and then filter the markdown files based on that path\n const match = file.match(/pages\\/(.*).mdx?$/);\n const path = match?.at(1);\n\n if (!path) return [];\n\n const pathSegments = path.split(\"/\");\n const isIndexFile = pathSegments.at(-1) === \"index\";\n const routePath = isIndexFile\n ? pathSegments.slice(0, -1).join(\"/\")\n : path;\n\n return {\n path: routePath,\n lazy: async () => {\n const { MdxPage } = await import(\"./MdxPage.js\");\n const { default: Component, ...props } = await importPromise();\n return {\n element: (\n <MdxPage\n mdxComponent={Component}\n {...props}\n defaultOptions={defaultOptions}\n />\n ),\n };\n },\n } satisfies RouteObject;\n },\n );\n\n const rootRoutes: RouteObject[] = Array.from(\n new Set(routes.map((route) => route.path.split(\"/\").at(0))),\n ).map((dir) => ({\n path: `/${dir}`,\n element: <Redirect />,\n }));\n\n return [...routes, ...rootRoutes];\n};\n\nconst Redirect = () => {\n const navItem = useTopNavigationItem();\n\n if (!navItem) return null;\n\n return traverseNavigation(navItem, (node, fullPath) => {\n if (\"children\" in node || !isPathItem(node)) return;\n return <Navigate to={fullPath} replace />;\n });\n};\n","import type { Toc } from \"@stefanprobst/rehype-extract-toc\";\nimport type { MDXProps } from \"mdx/types.js\";\nimport type { DevPortalPlugin } from \"../../core/plugins.js\";\nimport { generateRoutes } from \"./generateRoutes.js\";\n\nexport type MarkdownPluginOptions = {\n markdownFiles: Record<string, () => Promise<MDXImport>>;\n defaultOptions?: MarkdownPluginDefaultOptions;\n};\nexport type MarkdownPluginDefaultOptions = Pick<\n Frontmatter,\n \"toc\" | \"disablePager\"\n>;\n\nexport type Frontmatter = {\n title?: string;\n description?: string;\n category?: string;\n toc?: boolean;\n disablePager?: boolean;\n};\n\nexport type MDXImport = {\n tableOfContents: Toc;\n frontmatter: Frontmatter;\n default: (props: MDXProps) => JSX.Element;\n};\n\nexport const markdownPlugin = ({\n markdownFiles,\n defaultOptions,\n}: MarkdownPluginOptions): DevPortalPlugin => ({\n getRoutes: () => generateRoutes(markdownFiles, defaultOptions),\n});\n"],"names":["generateRoutes","markdownFiles","defaultOptions","routes","file","importPromise","match","path","pathSegments","MdxPage","Component","props","jsx","rootRoutes","route","dir","Redirect","navItem","useTopNavigationItem","traverseNavigation","node","fullPath","isPathItem","Navigate","markdownPlugin"],"mappings":";;AAUa,MAAAA,IAAiB,CAC5BC,GACAC,MACkB;AAClB,QAAMC,IAAS,OAAO,QAAQF,CAAa,EAAE;AAAA,IAC3C,CAAC,CAACG,GAAMC,CAAa,MAAM;AAEnB,YAAAC,IAAQF,EAAK,MAAM,mBAAmB,GACtCG,IAAOD,KAAA,gBAAAA,EAAO,GAAG;AAEnB,UAAA,CAACC,EAAM,QAAO;AAEZ,YAAAC,IAAeD,EAAK,MAAM,GAAG;AAM5B,aAAA;AAAA,QACL,MANkBC,EAAa,GAAG,EAAE,MAAM,UAExCA,EAAa,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,IAClCD;AAAA,QAIF,MAAM,YAAY;AAChB,gBAAM,EAAE,SAAAE,EAAA,IAAY,MAAM,OAAO,uBAAc,GACzC,EAAE,SAASC,GAAW,GAAGC,EAAM,IAAI,MAAMN;AACxC,iBAAA;AAAA,YACL,SACEO,gBAAAA,EAAA;AAAA,cAACH;AAAA,cAAA;AAAA,gBACC,cAAcC;AAAA,gBACb,GAAGC;AAAA,gBACJ,gBAAAT;AAAA,cAAA;AAAA,YACF;AAAA,UAAA;AAAA,QAGN;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA,GAGIW,IAA4B,MAAM;AAAA,IACtC,IAAI,IAAIV,EAAO,IAAI,CAACW,MAAUA,EAAM,KAAK,MAAM,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAAA,EAAA,EAC1D,IAAI,CAACC,OAAS;AAAA,IACd,MAAM,IAAIA,CAAG;AAAA,IACb,+BAAUC,GAAS,EAAA;AAAA,EACnB,EAAA;AAEF,SAAO,CAAC,GAAGb,GAAQ,GAAGU,CAAU;AAClC,GAEMG,IAAW,MAAM;AACrB,QAAMC,IAAUC;AAEZ,SAACD,IAEEE,EAAmBF,GAAS,CAACG,GAAMC,MAAa;AACrD,QAAI,gBAAcD,KAAQ,CAACE,EAAWF,CAAI;AAC1C,aAAQR,gBAAAA,EAAAA,IAAAW,GAAA,EAAS,IAAIF,GAAU,SAAO,GAAC,CAAA;AAAA,EAAA,CACxC,IALoB;AAMvB,GCtCaG,IAAiB,CAAC;AAAA,EAC7B,eAAAvB;AAAA,EACA,gBAAAC;AACF,OAA+C;AAAA,EAC7C,WAAW,MAAMF,EAAeC,GAAeC,CAAc;AAC/D;"}
1
+ {"version":3,"file":"zudoku.plugin-markdown.js","sources":["../src/lib/plugins/markdown/generateRoutes.tsx","../src/lib/plugins/markdown/index.tsx"],"sourcesContent":["import { type RouteObject } from \"react-router-dom\";\n\nimport {\n MarkdownPluginDefaultOptions,\n MarkdownPluginOptions,\n} from \"./index.js\";\n\nexport const generateRoutes = (\n markdownFiles: MarkdownPluginOptions[\"markdownFiles\"],\n defaultOptions?: MarkdownPluginDefaultOptions,\n): RouteObject[] =>\n Object.entries(markdownFiles).flatMap(([file, importPromise]) => {\n // @todo we can pass in the folder name and then filter the markdown files based on that path\n const match = file.match(/pages\\/(.*).mdx?$/);\n const path = match?.at(1);\n\n if (!path) return [];\n\n const pathSegments = path.split(\"/\");\n const isIndexFile = pathSegments.at(-1) === \"index\";\n const routePath = isIndexFile ? pathSegments.slice(0, -1).join(\"/\") : path;\n\n return {\n path: routePath,\n lazy: async () => {\n const { MdxPage } = await import(\"./MdxPage.js\");\n const { default: Component, ...props } = await importPromise();\n return {\n element: (\n <MdxPage\n mdxComponent={Component}\n {...props}\n defaultOptions={defaultOptions}\n />\n ),\n };\n },\n } satisfies RouteObject;\n });\n","import type { Toc } from \"@stefanprobst/rehype-extract-toc\";\nimport type { MDXProps } from \"mdx/types.js\";\nimport type { DevPortalPlugin } from \"../../core/plugins.js\";\nimport { generateRoutes } from \"./generateRoutes.js\";\n\nexport type MarkdownPluginOptions = {\n markdownFiles: Record<string, () => Promise<MDXImport>>;\n defaultOptions?: MarkdownPluginDefaultOptions;\n};\nexport type MarkdownPluginDefaultOptions = Pick<\n Frontmatter,\n \"toc\" | \"disablePager\"\n>;\n\nexport type Frontmatter = {\n title?: string;\n description?: string;\n category?: string;\n toc?: boolean;\n disablePager?: boolean;\n};\n\nexport type MDXImport = {\n tableOfContents: Toc;\n frontmatter: Frontmatter;\n default: (props: MDXProps) => JSX.Element;\n};\n\nexport const markdownPlugin = ({\n markdownFiles,\n defaultOptions,\n}: MarkdownPluginOptions): DevPortalPlugin => ({\n getRoutes: () => generateRoutes(markdownFiles, defaultOptions),\n});\n"],"names":["generateRoutes","markdownFiles","defaultOptions","file","importPromise","match","path","pathSegments","MdxPage","Component","props","jsx","markdownPlugin"],"mappings":";AAOO,MAAMA,IAAiB,CAC5BC,GACAC,MAEA,OAAO,QAAQD,CAAa,EAAE,QAAQ,CAAC,CAACE,GAAMC,CAAa,MAAM;AAEzD,QAAAC,IAAQF,EAAK,MAAM,mBAAmB,GACtCG,IAAOD,KAAA,gBAAAA,EAAO,GAAG;AAEnB,MAAA,CAACC,EAAM,QAAO;AAEZ,QAAAC,IAAeD,EAAK,MAAM,GAAG;AAI5B,SAAA;AAAA,IACL,MAJkBC,EAAa,GAAG,EAAE,MAAM,UACZA,EAAa,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,IAAID;AAAA,IAIpE,MAAM,YAAY;AAChB,YAAM,EAAE,SAAAE,EAAA,IAAY,MAAM,OAAO,uBAAc,GACzC,EAAE,SAASC,GAAW,GAAGC,EAAM,IAAI,MAAMN;AACxC,aAAA;AAAA,QACL,SACEO,gBAAAA,EAAA;AAAA,UAACH;AAAA,UAAA;AAAA,YACC,cAAcC;AAAA,YACb,GAAGC;AAAA,YACJ,gBAAAR;AAAA,UAAA;AAAA,QACF;AAAA,MAAA;AAAA,IAGN;AAAA,EAAA;AAEJ,CAAC,GCVUU,IAAiB,CAAC;AAAA,EAC7B,eAAAX;AAAA,EACA,gBAAAC;AACF,OAA+C;AAAA,EAC7C,WAAW,MAAMF,EAAeC,GAAeC,CAAc;AAC/D;"}
@@ -1,12 +1,14 @@
1
- import "./jsx-runtime-CJBdjYYx.js";
2
- import { o as f } from "./index-Bl6YeerK.js";
1
+ import "./jsx-runtime-B6kdoens.js";
2
+ import { o as l } from "./index-CgCPw6Jn.js";
3
3
  import "./urql-DrBfkb92.js";
4
- import "./DevPortalProvider-BWeAysxF.js";
4
+ import "./DevPortalProvider-CTxoCHIT.js";
5
5
  import "zudoku/openapi-worker";
6
- import "./Combination-C442XfGG.js";
7
- import "./Markdown-QsZ-PHET.js";
6
+ import "./Combination-DkycFHkm.js";
7
+ import "./Markdown-B_Gax7at.js";
8
+ import "./joinPath-VeNuJa7y.js";
8
9
  import "./router-BiRCp01d.js";
10
+ import "./index-7kcHaXD6.js";
9
11
  export {
10
- f as openApiPlugin
12
+ l as openApiPlugin
11
13
  };
12
14
  //# sourceMappingURL=zudoku.plugin-openapi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zudoku.plugin-openapi.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
1
+ {"version":3,"file":"zudoku.plugin-openapi.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.3.0-dev.82",
3
+ "version": "0.3.0-dev.84",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -102,9 +102,11 @@
102
102
  "class-variance-authority": "0.7.0",
103
103
  "dotenv": "16.4.5",
104
104
  "express": "4.19.2",
105
+ "glob": "^11.0.0",
105
106
  "graphql": "16.9.0",
106
107
  "graphql-type-json": "0.3.2",
107
108
  "graphql-yoga": "5.2.0",
109
+ "gray-matter": "^4.0.3",
108
110
  "loglevel": "^1.9.1",
109
111
  "lru-cache": "10.2.0",
110
112
  "mdx": "0.3.1",
@@ -170,6 +172,7 @@
170
172
  "react-markdown": "9.0.1",
171
173
  "react-router-dom": "6.25.1",
172
174
  "rollup-plugin-visualizer": "^5.12.0",
175
+ "tsx": "4.16.5",
173
176
  "typescript": "5.5.3"
174
177
  },
175
178
  "peerDependencies": {
package/src/app/demo.tsx CHANGED
@@ -32,14 +32,15 @@ createRoot(document.getElementById("root")!).render(
32
32
  },
33
33
  pageTitle: "Developer Portal",
34
34
  }}
35
- navigation={[
35
+ topNavigation={[
36
36
  {
37
+ id: "demo",
37
38
  label: "API Reference",
38
- path: "/demo",
39
- categories: [],
40
39
  },
41
40
  ]}
42
- plugins={[openApiPlugin({ type: "url", input: apiUrl!, path: "/demo" })]}
41
+ plugins={[
42
+ openApiPlugin({ type: "url", input: apiUrl!, navigationId: "demo" }),
43
+ ]}
43
44
  />
44
45
  </StrictMode>,
45
46
  );
package/src/app/main.css CHANGED
@@ -137,10 +137,10 @@
137
137
  --slide-offset: -0.75rem;
138
138
  @apply overflow-hidden;
139
139
  }
140
- [data-animate="true"] .CollapsibleContent[data-state="open"] {
140
+ .CollapsibleContent[data-state="open"] {
141
141
  animation: slideDown 300ms var(--easing);
142
142
  }
143
- [data-animate="true"] .CollapsibleContent[data-state="closed"] {
143
+ .CollapsibleContent[data-state="closed"] {
144
144
  animation: slideUp 300ms var(--easing);
145
145
  }
146
146
 
package/src/app/main.tsx CHANGED
@@ -4,6 +4,7 @@ import { configuredApiPlugins } from "virtual:zudoku-api-plugins";
4
4
  import { configuredAuthProvider } from "virtual:zudoku-auth";
5
5
  import { configuredDocsPlugins } from "virtual:zudoku-docs-plugins";
6
6
  import { configuredRedirectPlugin } from "virtual:zudoku-redirect-plugin";
7
+ import { configuredSidebar } from "virtual:zudoku-sidebar";
7
8
  import "virtual:zudoku-theme.css";
8
9
  import { DevPortal, Layout, RouterError } from "zudoku/components";
9
10
  import { isNavigationPlugin } from "zudoku/internal";
@@ -35,8 +36,9 @@ export const convertZudokuConfigToOptions = (
35
36
  title: "%s | Developer Portal",
36
37
  ...config.metadata,
37
38
  },
39
+ sidebars: configuredSidebar,
40
+ topNavigation: config.topNavigation,
38
41
  mdx: config.mdx,
39
- navigation: config.navigation ?? [],
40
42
  authentication: configuredAuthProvider,
41
43
  plugins: [
42
44
  ...configuredDocsPlugins,
@@ -38,14 +38,15 @@ createRoot(root).render(
38
38
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
39
39
  pageTitle: pageTitle ?? "Developer Portal",
40
40
  }}
41
- navigation={[
41
+ topNavigation={[
42
42
  {
43
+ id: "demo",
43
44
  label: "API Reference",
44
- path: "/",
45
- categories: [],
46
45
  },
47
46
  ]}
48
- plugins={[openApiPlugin({ type: "url", input: apiUrl!, path: "/" })]}
47
+ plugins={[
48
+ openApiPlugin({ type: "url", input: apiUrl!, navigationId: "/" }),
49
+ ]}
49
50
  />
50
51
  </StrictMode>,
51
52
  );
@@ -28,7 +28,7 @@ import {
28
28
  DEFAULT_COMPONENTS,
29
29
  } from "./context/ComponentsContext.js";
30
30
  import { DevPortalProvider } from "./context/DevPortalProvider.js";
31
- import { ThemeProvider } from "./context/ThemeContext.js";
31
+ import { ThemeProvider } from "./context/ThemeProvider.js";
32
32
  import { ViewportAnchorProvider } from "./context/ViewportAnchorContext.js";
33
33
  import { SlotletProvider } from "./SlotletProvider.js";
34
34
 
@@ -3,7 +3,7 @@ import { memo } from "react";
3
3
 
4
4
  import { Link, useLocation } from "react-router-dom";
5
5
  import { useAuth } from "../authentication/hook.js";
6
- import { isProfileMenuPlugin, NavigationItem } from "../core/plugins.js";
6
+ import { isProfileMenuPlugin, ProfileNavigationItem } from "../core/plugins.js";
7
7
  import { Button } from "../ui/Button.js";
8
8
  import {
9
9
  DropdownMenu,
@@ -22,7 +22,7 @@ import { TopNavigation } from "./TopNavigation.js";
22
22
  import { useDevPortal } from "./context/DevPortalProvider.js";
23
23
  import { useTheme } from "./context/ThemeContext.js";
24
24
 
25
- const RecursiveMenu = ({ item }: { item: NavigationItem }) => {
25
+ const RecursiveMenu = ({ item }: { item: ProfileNavigationItem }) => {
26
26
  return item.children ? (
27
27
  <DropdownMenuSub key={item.label}>
28
28
  <DropdownMenuSubTrigger>{item.label}</DropdownMenuSubTrigger>
@@ -7,7 +7,7 @@ import { useScrollToTop } from "../util/useScrollToTop.js";
7
7
  import { useDevPortal } from "./context/DevPortalProvider.js";
8
8
  import { useViewportAnchor } from "./context/ViewportAnchorContext.js";
9
9
  import { Header } from "./Header.js";
10
- import { SideNavigation } from "./navigation/SideNavigation.js";
10
+ import { Sidebar } from "./navigation/Sidebar.js";
11
11
  import { Slotlet } from "./SlotletProvider.js";
12
12
  import { Spinner } from "./Spinner.js";
13
13
 
@@ -47,7 +47,7 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
47
47
  </div>
48
48
  }
49
49
  >
50
- <SideNavigation />
50
+ <Sidebar />
51
51
  <main
52
52
  className={cn(
53
53
  "dark:border-white/10 translate-x-0 h-full",
@@ -4,17 +4,17 @@ import { NavLink } from "react-router-dom";
4
4
  import { useDevPortal } from "./context/DevPortalProvider.js";
5
5
 
6
6
  export const TopNavigation = () => {
7
- const { navigation } = useDevPortal();
7
+ const { topNavigation } = useDevPortal();
8
8
 
9
- // Hide tope nav if there is only one item
10
- if (navigation.length <= 1) {
9
+ // Hide top nav if there is only one item
10
+ if (topNavigation.length <= 1) {
11
11
  return null;
12
12
  }
13
13
 
14
14
  return (
15
15
  <nav className="border-b text-sm px-12 h-[--top-nav-height]">
16
16
  <ul className="flex flex-row items-center gap-8">
17
- {navigation.map((item) => (
17
+ {topNavigation.map((item) => (
18
18
  <li key={item.label}>
19
19
  <NavLink
20
20
  className={({ isActive }) =>
@@ -25,7 +25,7 @@ export const TopNavigation = () => {
25
25
  : "border-transparent text-foreground/75 hover:text-foreground hover:border-accent-foreground/25",
26
26
  )
27
27
  }
28
- to={item.path}
28
+ to={item.id}
29
29
  >
30
30
  {item.label}
31
31
  </NavLink>
@@ -1,8 +1,7 @@
1
1
  import { useQuery, useSuspenseQuery } from "@tanstack/react-query";
2
2
  import { createContext, useContext } from "react";
3
- import { matchPath, useLocation } from "react-router-dom";
3
+ import { useLocation } from "react-router-dom";
4
4
  import { DevPortalContext } from "../../core/DevPortalContext.js";
5
- import { traverseNavigation } from "../../util/traverseNavigation.js";
6
5
 
7
6
  const DevPortalReactContext = createContext<DevPortalContext | undefined>(
8
7
  undefined,
@@ -29,43 +28,27 @@ export const useApiIdentities = () => {
29
28
  };
30
29
 
31
30
  export const useTopNavigationItem = () => {
32
- const { navigation } = useDevPortal();
31
+ const { topNavigation } = useDevPortal();
33
32
  const location = useLocation();
34
33
 
35
- // The `/` path needs this logic because it would always match:
36
- // Only if a leaf node actually matches the current path it is active
37
- for (const item of navigation) {
38
- const foundNavItem = traverseNavigation(item, (_node, fullPath) => {
39
- if (location.pathname === fullPath) {
40
- return item;
41
- }
42
- });
34
+ const firstPart = location.pathname.split("/").at(1);
35
+ if (!firstPart) return;
43
36
 
44
- if (foundNavItem) {
45
- return foundNavItem;
46
- }
47
- }
48
- if (location.pathname === "/") {
49
- return navigation.find((item) => item.path === "/");
50
- }
51
-
52
- return navigation.find(
53
- (item) =>
54
- item.path !== "/" &&
55
- matchPath({ path: item.path, end: false }, location.pathname),
56
- );
37
+ return topNavigation.find((item) => item.id === firstPart);
57
38
  };
58
39
 
59
40
  export const useNavigation = () => {
60
- const { getNavigation } = useDevPortal();
41
+ const { getPluginNavigation, sidebars } = useDevPortal();
61
42
  const navItem = useTopNavigationItem();
62
-
63
- const path = navItem?.path ?? "";
43
+ const path = navItem?.id;
44
+ const currentSidebar = path ? sidebars[path] ?? [] : [];
64
45
 
65
46
  return useSuspenseQuery({
66
47
  queryFn: async () => {
48
+ const pluginSidebar = path ? await getPluginNavigation(path) : [];
49
+
67
50
  return {
68
- items: [...(navItem?.categories ?? []), ...(await getNavigation(path))],
51
+ items: [...currentSidebar, ...pluginSidebar],
69
52
  currentTopNavItem: navItem,
70
53
  };
71
54
  },
@@ -1,46 +1,8 @@
1
- import {
2
- createContext,
3
- type ReactNode,
4
- useCallback,
5
- useContext,
6
- useEffect,
7
- useState,
8
- } from "react";
1
+ import { createContext, useContext } from "react";
9
2
 
10
- const ThemeContext = createContext<readonly [boolean, () => void]>([
3
+ export const ThemeContext = createContext<readonly [boolean, () => void]>([
11
4
  false,
12
5
  () => {},
13
6
  ]);
14
7
 
15
- export const useTheme = () => {
16
- const context = useContext(ThemeContext);
17
- if (!context) {
18
- throw new Error("useTheme must be used within a ThemeProvider");
19
- }
20
- return context;
21
- };
22
-
23
- export const ThemeProvider = (props: { children: ReactNode }) => {
24
- const [dark, setDark] = useState(false);
25
-
26
- // On mount, read the preferred theme from the persistence
27
- useEffect(() => {
28
- const theme = localStorage.getItem("theme");
29
- const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
30
- const isDark = theme === "dark" || (!theme && prefersDark.matches);
31
-
32
- setDark(isDark);
33
- }, [dark]);
34
-
35
- // To toggle between dark and light modes
36
- const toggle = useCallback(() => {
37
- const toggled = !dark;
38
- document.documentElement.classList.toggle("dark", toggled);
39
- localStorage.setItem("theme", toggled ? "dark" : "light");
40
- setDark(toggled);
41
- }, [dark]);
42
-
43
- const value = [dark, toggle] as const;
44
-
45
- return <ThemeContext.Provider value={value} {...props} />;
46
- };
8
+ export const useTheme = () => useContext(ThemeContext);
@@ -0,0 +1,27 @@
1
+ import { ReactNode, useCallback, useEffect, useState } from "react";
2
+ import { ThemeContext } from "./ThemeContext.js";
3
+
4
+ export const ThemeProvider = (props: { children: ReactNode }) => {
5
+ const [dark, setDark] = useState(false);
6
+
7
+ // On mount, read the preferred theme from the persistence
8
+ useEffect(() => {
9
+ const theme = localStorage.getItem("theme");
10
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
11
+ const isDark = theme === "dark" || (!theme && prefersDark.matches);
12
+
13
+ setDark(isDark);
14
+ }, [dark]);
15
+
16
+ // To toggle between dark and light modes
17
+ const toggle = useCallback(() => {
18
+ const toggled = !dark;
19
+ document.documentElement.classList.toggle("dark", toggled);
20
+ localStorage.setItem("theme", toggled ? "dark" : "light");
21
+ setDark(toggled);
22
+ }, [dark]);
23
+
24
+ const value = [dark, toggle] as const;
25
+
26
+ return <ThemeContext.Provider value={value} {...props} />;
27
+ };
@@ -2,23 +2,23 @@ import { useRef } from "react";
2
2
 
3
3
  import { useNavigation } from "../context/DevPortalProvider.js";
4
4
  import { Slotlet } from "../SlotletProvider.js";
5
- import { SideNavigationCategory } from "./SideNavigationCategory.js";
6
- import { SideNavigationWrapper } from "./SideNavigationWrapper.js";
5
+ import { SidebarItem } from "./SidebarItem.js";
6
+ import { SidebarWrapper } from "./SidebarWrapper.js";
7
7
 
8
- export const SideNavigation = () => {
8
+ export const Sidebar = () => {
9
9
  const navRef = useRef<HTMLDivElement | null>(null);
10
10
  const navigation = useNavigation();
11
11
 
12
12
  return (
13
- <SideNavigationWrapper
13
+ <SidebarWrapper
14
14
  ref={navRef}
15
15
  pushMainContent={navigation.data.items.length > 0}
16
16
  >
17
17
  <Slotlet name="zudoku-before-navigation" />
18
- {navigation.data.items.map((category) => (
19
- <SideNavigationCategory key={category.label} category={category} />
18
+ {navigation.data.items.map((item) => (
19
+ <SidebarItem key={item.label} item={item} />
20
20
  ))}
21
21
  <Slotlet name="zudoku-after-navigation" />
22
- </SideNavigationWrapper>
22
+ </SidebarWrapper>
23
23
  );
24
24
  };
@@ -0,0 +1,40 @@
1
+ import { cn } from "../../util/cn.js";
2
+
3
+ export const TextColorMap = {
4
+ green: "text-green-600",
5
+ blue: "text-sky-600",
6
+ yellow: "text-yellow-600",
7
+ red: "text-red-600",
8
+ purple: "text-purple-600",
9
+ indigo: "text-indigo-600",
10
+ gray: "text-gray-600",
11
+ };
12
+
13
+ export const ColorMap = {
14
+ green: "bg-green-400 dark:bg-green-800",
15
+ blue: "bg-sky-400 dark:bg-sky-800",
16
+ yellow: "bg-yellow-400 dark:bg-yellow-800",
17
+ red: "bg-red-400 dark:bg-red-800",
18
+ purple: "bg-purple-400 dark:bg-purple-600",
19
+ indigo: "bg-indigo-400 dark:bg-indigo-600",
20
+ gray: "bg-gray-400 dark:bg-gray-600",
21
+ };
22
+
23
+ export const SidebarBadge = ({
24
+ color,
25
+ label,
26
+ }: {
27
+ color: keyof typeof ColorMap;
28
+ label: string;
29
+ }) => {
30
+ return (
31
+ <span
32
+ className={cn(
33
+ "mt-0.5 flex items-center duration-200 transition-opacity text-center uppercase font-mono text-[0.65rem] font-bold rounded text-background dark:text-zinc-50 h-4 px-1",
34
+ ColorMap[color],
35
+ )}
36
+ >
37
+ {label}
38
+ </span>
39
+ );
40
+ };
@@ -0,0 +1,105 @@
1
+ import * as Collapsible from "@radix-ui/react-collapsible";
2
+ import { ChevronRightIcon } from "lucide-react";
3
+ import { useEffect, useState } from "react";
4
+ import { NavLink } from "react-router-dom";
5
+ import type { ResolvedSidebarItemCategory } from "../../../config/validators/ResolvedSidebarSchema.js";
6
+ import { cn } from "../../util/cn.js";
7
+ import { joinPath } from "../../util/joinPath.js";
8
+ import { useTopNavigationItem } from "../context/DevPortalProvider.js";
9
+ import { navigationListItem, SidebarItem } from "./SidebarItem.js";
10
+ import { useIsCategoryOpen } from "./utils.js";
11
+
12
+ export const SidebarCategory = ({
13
+ category,
14
+ level,
15
+ }: {
16
+ category: ResolvedSidebarItemCategory;
17
+ level: number;
18
+ }) => {
19
+ const topNavItem = useTopNavigationItem();
20
+ const isCategoryOpen = useIsCategoryOpen(category);
21
+
22
+ const isCollapsible = category.collapsible ?? true;
23
+ const isCollapsed = category.collapsed ?? true;
24
+ const isDefaultOpen = Boolean(
25
+ !isCollapsible || !isCollapsed || isCategoryOpen,
26
+ );
27
+ const [open, setOpen] = useState(isDefaultOpen);
28
+
29
+ useEffect(() => {
30
+ // this is triggered when an item from the sidebar is clicked
31
+ // and the sidebar, enclosing this item, is not opened
32
+ if (isCategoryOpen) {
33
+ setOpen(true);
34
+ }
35
+ }, [isCategoryOpen]);
36
+
37
+ const ToggleButton = isCollapsible && (
38
+ <button
39
+ type="button"
40
+ onClick={(e) => {
41
+ e.preventDefault();
42
+ setOpen((prev) => !prev);
43
+ }}
44
+ >
45
+ <ChevronRightIcon
46
+ size={16}
47
+ className="transition shrink-0 group-data-[state=open]:rotate-90"
48
+ />
49
+ </button>
50
+ );
51
+
52
+ return (
53
+ <Collapsible.Root
54
+ className={cn("flex flex-col", level === 0 && "-mx-[--padding-nav-item]")}
55
+ defaultOpen={isDefaultOpen}
56
+ open={open}
57
+ onOpenChange={() => setOpen(true)}
58
+ >
59
+ <Collapsible.Trigger
60
+ className={cn(
61
+ "group text-start",
62
+ navigationListItem({ isActive: false, isTopLevel: level === 0 }),
63
+ isCollapsible
64
+ ? "cursor-pointer"
65
+ : "cursor-default hover:bg-transparent",
66
+ )}
67
+ asChild
68
+ disabled={!isCollapsible}
69
+ >
70
+ {category.link?.type === "doc" ? (
71
+ <NavLink to={joinPath(topNavItem?.id, category.link.id)}>
72
+ {({ isActive }) => (
73
+ <div
74
+ className={cn(
75
+ "flex items-center gap-2 justify-between w-full",
76
+ isActive ? "text-primary font-medium" : "text-foreground/80",
77
+ )}
78
+ >
79
+ <div className="truncate">{category.label}</div>
80
+ {ToggleButton}
81
+ </div>
82
+ )}
83
+ </NavLink>
84
+ ) : (
85
+ <div className="flex items-center justify-between w-full">
86
+ <div className="flex gap-2 truncate w-full">{category.label}</div>
87
+ {ToggleButton}
88
+ </div>
89
+ )}
90
+ </Collapsible.Trigger>
91
+ <Collapsible.Content className="CollapsibleContent ms-[calc(var(--padding-nav-item)*1.125)]">
92
+ <ul className="mt-1 border-l ps-2">
93
+ {category.items.map((item) => (
94
+ <SidebarItem
95
+ key={item.label}
96
+ level={level + 1}
97
+ item={item}
98
+ // activeAnchor={activeAnchor}
99
+ />
100
+ ))}
101
+ </ul>
102
+ </Collapsible.Content>
103
+ </Collapsible.Root>
104
+ );
105
+ };
@@ -0,0 +1,96 @@
1
+ import { cva } from "class-variance-authority";
2
+ import { ExternalLinkIcon } from "lucide-react";
3
+ import { NavLink } from "react-router-dom";
4
+
5
+ import type { ResolvedSidebarItem } from "../../../config/validators/ResolvedSidebarSchema.js";
6
+ import { cn } from "../../util/cn.js";
7
+ import { joinPath } from "../../util/joinPath.js";
8
+ import { AnchorLink } from "../AnchorLink.js";
9
+ import { useTopNavigationItem } from "../context/DevPortalProvider.js";
10
+ import { useViewportAnchor } from "../context/ViewportAnchorContext.js";
11
+ import { SidebarBadge } from "./SidebarBadge.js";
12
+ import { SidebarCategory } from "./SidebarCategory.js";
13
+
14
+ export const navigationListItem = cva(
15
+ "flex px-[--padding-nav-item] py-1.5 rounded-lg hover:bg-accent transition-colors duration-300",
16
+ {
17
+ variants: {
18
+ isTopLevel: {
19
+ true: "font-semibold",
20
+ },
21
+ isActive: {
22
+ true: "text-primary font-medium",
23
+ false: "text-foreground/80",
24
+ },
25
+ isMuted: {
26
+ true: "text-foreground/30",
27
+ false: "",
28
+ },
29
+ },
30
+ },
31
+ );
32
+
33
+ export const DATA_ANCHOR_ATTR = "data-anchor";
34
+
35
+ export const SidebarItem = ({
36
+ item,
37
+ level = 0,
38
+ }: {
39
+ item: ResolvedSidebarItem;
40
+ basePath?: string;
41
+ level?: number;
42
+ }) => {
43
+ const topNavItem = useTopNavigationItem();
44
+ const { activeAnchor } = useViewportAnchor();
45
+
46
+ switch (item.type) {
47
+ case "category":
48
+ return <SidebarCategory category={item} level={level} />;
49
+ case "doc":
50
+ return (
51
+ <NavLink
52
+ className={({ isActive }) =>
53
+ navigationListItem({ isActive, isTopLevel: level === 0 })
54
+ }
55
+ to={joinPath(topNavItem?.id, item.id)}
56
+ >
57
+ {item.label}
58
+ {item.badge && <SidebarBadge {...item.badge} />}
59
+ </NavLink>
60
+ );
61
+ case "link":
62
+ return item.href.startsWith("#") ? (
63
+ <AnchorLink
64
+ to={item.href}
65
+ {...{ [DATA_ANCHOR_ATTR]: item.href.slice(1) }}
66
+ className={cn(
67
+ "flex gap-2.5 justify-between",
68
+ level === 0 && "-mx-[--padding-nav-item]",
69
+ navigationListItem({
70
+ isActive: item.href.slice(1) === activeAnchor,
71
+ }),
72
+ )}
73
+ >
74
+ {item.label}
75
+ {item.badge && <SidebarBadge {...item.badge} />}
76
+ </AnchorLink>
77
+ ) : (
78
+ <a
79
+ className={cn(
80
+ navigationListItem({ isTopLevel: level === 0 }),
81
+ "block",
82
+ )}
83
+ href={item.href}
84
+ target="_blank"
85
+ rel="noopener noreferrer"
86
+ >
87
+ <span className="whitespace-normal">{item.label}</span>
88
+ {/* This prevents that the icon would be positioned in its own line if the text fills an line entirely */}
89
+ <span className="whitespace-nowrap">
90
+ &nbsp;
91
+ <ExternalLinkIcon className="inline ml-1" size={12} />
92
+ </span>
93
+ </a>
94
+ );
95
+ }
96
+ };
@@ -1,7 +1,7 @@
1
1
  import { forwardRef, type PropsWithChildren } from "react";
2
2
  import { cn } from "../../util/cn.js";
3
3
 
4
- export const SideNavigationWrapper = forwardRef<
4
+ export const SidebarWrapper = forwardRef<
5
5
  HTMLDivElement,
6
6
  PropsWithChildren<{ pushMainContent?: boolean; className?: string }>
7
7
  >(function SideNavigation({ children, className, pushMainContent }, ref) {