mintlify 1.0.6 → 1.0.8

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 (286) hide show
  1. package/README.md +27 -3
  2. package/bin/browser.js +24 -0
  3. package/bin/browser.js.map +1 -0
  4. package/bin/constants.js +8 -0
  5. package/bin/constants.js.map +1 -0
  6. package/bin/dev/getOpenApiContext.js +46 -0
  7. package/bin/dev/getOpenApiContext.js.map +1 -0
  8. package/bin/dev/index.js +164 -0
  9. package/bin/dev/index.js.map +1 -0
  10. package/bin/dev/injectNav.js +97 -0
  11. package/bin/dev/injectNav.js.map +1 -0
  12. package/bin/dev/slugToTitle.js +8 -0
  13. package/bin/dev/slugToTitle.js.map +1 -0
  14. package/bin/downloadImage.js +27 -0
  15. package/bin/downloadImage.js.map +1 -0
  16. package/bin/index.js +49 -106
  17. package/bin/index.js.map +1 -1
  18. package/bin/init-command/index.js +51 -0
  19. package/bin/init-command/index.js.map +1 -0
  20. package/bin/init-command/templates.js +41 -0
  21. package/bin/init-command/templates.js.map +1 -0
  22. package/bin/local-preview/categorizeFiles.js +56 -0
  23. package/bin/local-preview/categorizeFiles.js.map +1 -0
  24. package/bin/local-preview/getOpenApiContext.js +46 -0
  25. package/bin/local-preview/getOpenApiContext.js.map +1 -0
  26. package/bin/local-preview/index.js +138 -0
  27. package/bin/local-preview/index.js.map +1 -0
  28. package/bin/local-preview/injectFavicons.js +72 -0
  29. package/bin/local-preview/injectFavicons.js.map +1 -0
  30. package/bin/local-preview/listener.js +112 -0
  31. package/bin/local-preview/listener.js.map +1 -0
  32. package/bin/local-preview/metadata.js +121 -0
  33. package/bin/local-preview/metadata.js.map +1 -0
  34. package/bin/local-preview/mintConfigFile.js +43 -0
  35. package/bin/local-preview/mintConfigFile.js.map +1 -0
  36. package/bin/local-preview/openApiCheck.js +16 -0
  37. package/bin/local-preview/openApiCheck.js.map +1 -0
  38. package/bin/local-preview/slugToTitle.js +8 -0
  39. package/bin/local-preview/slugToTitle.js.map +1 -0
  40. package/bin/mint/client/.babel-plugin-macrosrc.json +5 -0
  41. package/bin/mint/client/.babelrc +4 -0
  42. package/bin/mint/client/.editorconfig +12 -0
  43. package/bin/mint/client/.eslintrc.json +7 -0
  44. package/bin/mint/client/.prettierignore +4 -0
  45. package/bin/mint/client/.prettierrc +14 -0
  46. package/bin/mint/client/.vscode/launch.json +28 -0
  47. package/bin/mint/client/README.md +46 -0
  48. package/bin/mint/client/jest.config.ts +195 -0
  49. package/bin/mint/client/next-env.d.ts +4 -0
  50. package/bin/mint/client/next.config.js +152 -0
  51. package/bin/mint/client/package.json +140 -0
  52. package/bin/mint/client/postcss.config.cjs +9 -0
  53. package/bin/mint/client/prebuild/faviconConfig.js +35 -0
  54. package/bin/mint/client/prebuild/getOpenApiContext.js +53 -0
  55. package/bin/mint/client/prebuild/index.js +117 -0
  56. package/bin/mint/client/prebuild/injectNav.js +115 -0
  57. package/bin/mint/client/prebuild/slugToTitle.js +7 -0
  58. package/bin/mint/client/rehype/withApiComponents.js +60 -0
  59. package/bin/mint/client/rehype/withCodeBlocks.js +54 -0
  60. package/bin/mint/client/rehype/withLayouts.js +113 -0
  61. package/bin/mint/client/rehype/withLinkRoles.js +13 -0
  62. package/bin/mint/client/rehype/withRawComponents.js +13 -0
  63. package/bin/mint/client/rehype/withStaticProps.js +25 -0
  64. package/bin/mint/client/rehype/withSyntaxHighlighting.js +60 -0
  65. package/bin/mint/client/remark/utils.js +369 -0
  66. package/bin/mint/client/remark/withFrames.js +55 -0
  67. package/bin/mint/client/remark/withImportsInjected.js +36 -0
  68. package/bin/mint/client/remark/withNextLinks.js +37 -0
  69. package/bin/mint/client/remark/withTableOfContents.js +71 -0
  70. package/bin/mint/client/scripts/local-to-docs.js +72 -0
  71. package/bin/mint/client/scripts/local.js +177 -0
  72. package/bin/mint/client/sentry.client.config.js +15 -0
  73. package/bin/mint/client/sentry.properties +4 -0
  74. package/bin/mint/client/sentry.server.config.js +15 -0
  75. package/bin/mint/client/src/analytics/AbstractAnalyticsImplementation.ts +50 -0
  76. package/bin/mint/client/src/analytics/AnalyticsContext.ts +5 -0
  77. package/bin/mint/client/src/analytics/AnalyticsMediator.ts +101 -0
  78. package/bin/mint/client/src/analytics/FakeAnalyticsMediator.ts +9 -0
  79. package/bin/mint/client/src/analytics/GA4Script.tsx +33 -0
  80. package/bin/mint/client/src/analytics/implementations/amplitude.ts +26 -0
  81. package/bin/mint/client/src/analytics/implementations/fathom.ts +38 -0
  82. package/bin/mint/client/src/analytics/implementations/ga4.ts +33 -0
  83. package/bin/mint/client/src/analytics/implementations/hotjar.ts +53 -0
  84. package/bin/mint/client/src/analytics/implementations/mixpanel-browser.d.ts +1 -0
  85. package/bin/mint/client/src/analytics/implementations/mixpanel.ts +52 -0
  86. package/bin/mint/client/src/analytics/implementations/posthog.ts +37 -0
  87. package/bin/mint/client/src/components/Accordion/Accordion.tsx +43 -0
  88. package/bin/mint/client/src/components/Accordion/index.ts +4 -0
  89. package/bin/mint/client/src/components/ApiExample.tsx +9 -0
  90. package/bin/mint/client/src/components/Card.tsx +51 -0
  91. package/bin/mint/client/src/components/CodeGroup.tsx +132 -0
  92. package/bin/mint/client/src/components/Editor.tsx +12 -0
  93. package/bin/mint/client/src/components/Expandable.tsx +40 -0
  94. package/bin/mint/client/src/components/Heading.tsx +84 -0
  95. package/bin/mint/client/src/components/Param.tsx +56 -0
  96. package/bin/mint/client/src/components/Request.tsx +19 -0
  97. package/bin/mint/client/src/components/ResponseField.tsx +33 -0
  98. package/bin/mint/client/src/components/TabBar.tsx +61 -0
  99. package/bin/mint/client/src/config.ts +115 -0
  100. package/bin/mint/client/src/css/bar-of-progress.css +10 -0
  101. package/bin/mint/client/src/css/base.css +29 -0
  102. package/bin/mint/client/src/css/font-awesome.css +7 -0
  103. package/bin/mint/client/src/css/fonts.css +44 -0
  104. package/bin/mint/client/src/css/main.css +11 -0
  105. package/bin/mint/client/src/css/prism.css +270 -0
  106. package/bin/mint/client/src/css/utilities.css +43 -0
  107. package/bin/mint/client/src/enums/components.ts +8 -0
  108. package/bin/mint/client/src/fonts/FiraCode-VF.woff +0 -0
  109. package/bin/mint/client/src/fonts/FiraCode-VF.woff2 +0 -0
  110. package/bin/mint/client/src/fonts/IBMPlexMono-Regular.ttf +0 -0
  111. package/bin/mint/client/src/fonts/IBMPlexMono-SemiBold.ttf +0 -0
  112. package/bin/mint/client/src/fonts/Inter-italic-latin.var.woff2 +0 -0
  113. package/bin/mint/client/src/fonts/Inter-roman-latin.var.woff2 +0 -0
  114. package/bin/mint/client/src/fonts/Pally-Variable.ttf +0 -0
  115. package/bin/mint/client/src/fonts/SourceSansPro-Regular.otf +0 -0
  116. package/bin/mint/client/src/fonts/SourceSerifPro-Regular.ttf +0 -0
  117. package/bin/mint/client/src/fonts/Synonym-Variable.ttf +0 -0
  118. package/bin/mint/client/src/fonts/Ubuntu-Mono-bold.woff2 +0 -0
  119. package/bin/mint/client/src/fonts/generated/IBMPlexMono-Regular-subset.woff2 +0 -0
  120. package/bin/mint/client/src/fonts/generated/IBMPlexMono-Regular-subset.zopfli.woff +0 -0
  121. package/bin/mint/client/src/fonts/generated/IBMPlexMono-Regular.module.css +11 -0
  122. package/bin/mint/client/src/fonts/generated/IBMPlexMono-SemiBold-subset.woff2 +0 -0
  123. package/bin/mint/client/src/fonts/generated/IBMPlexMono-SemiBold-subset.zopfli.woff +0 -0
  124. package/bin/mint/client/src/fonts/generated/IBMPlexMono-SemiBold.module.css +11 -0
  125. package/bin/mint/client/src/fonts/generated/Pally-Variable-subset.woff2 +0 -0
  126. package/bin/mint/client/src/fonts/generated/Pally-Variable-subset.zopfli.woff +0 -0
  127. package/bin/mint/client/src/fonts/generated/Pally-Variable.module.css +11 -0
  128. package/bin/mint/client/src/fonts/generated/SourceSerifPro-Regular-subset.woff2 +0 -0
  129. package/bin/mint/client/src/fonts/generated/SourceSerifPro-Regular-subset.zopfli.woff +0 -0
  130. package/bin/mint/client/src/fonts/generated/SourceSerifPro-Regular.module.css +11 -0
  131. package/bin/mint/client/src/fonts/generated/Synonym-Variable-subset.woff2 +0 -0
  132. package/bin/mint/client/src/fonts/generated/Synonym-Variable-subset.zopfli.woff +0 -0
  133. package/bin/mint/client/src/fonts/generated/Synonym-Variable.module.css +11 -0
  134. package/bin/mint/client/src/fonts/generated/TenorSans-Regular-subset.woff2 +0 -0
  135. package/bin/mint/client/src/fonts/generated/TenorSans-Regular-subset.zopfli.woff +0 -0
  136. package/bin/mint/client/src/fonts/generated/TenorSans-Regular.module.css +11 -0
  137. package/bin/mint/client/src/hooks/useActionKey.ts +20 -0
  138. package/bin/mint/client/src/hooks/useIsomorphicLayoutEffect.ts +3 -0
  139. package/bin/mint/client/src/hooks/useMedia.ts +27 -0
  140. package/bin/mint/client/src/hooks/usePrevNext.ts +34 -0
  141. package/bin/mint/client/src/hooks/useTop.ts +15 -0
  142. package/bin/mint/client/src/icons/CopyToClipboard.tsx +33 -0
  143. package/bin/mint/client/src/index.d.ts +1 -0
  144. package/bin/mint/client/src/layouts/ApiSupplemental.tsx +173 -0
  145. package/bin/mint/client/src/layouts/ContentsLayout.tsx +256 -0
  146. package/bin/mint/client/src/layouts/DocumentationLayout.tsx +44 -0
  147. package/bin/mint/client/src/layouts/OpenApiContent.tsx +301 -0
  148. package/bin/mint/client/src/layouts/SidebarLayout.tsx +412 -0
  149. package/bin/mint/client/src/layouts/UserFeedback.tsx +73 -0
  150. package/bin/mint/client/src/layouts/getGroupsInDivision.ts +25 -0
  151. package/bin/mint/client/src/layouts/isPathInGroupPages.ts +10 -0
  152. package/bin/mint/client/src/metadata.ts +58 -0
  153. package/bin/mint/client/src/openapi.ts +3 -0
  154. package/bin/mint/client/src/pages/404.tsx +73 -0
  155. package/bin/mint/client/src/pages/_app.tsx +138 -0
  156. package/bin/mint/client/src/pages/_document.tsx +57 -0
  157. package/bin/mint/client/src/pages/api/issue.ts +10 -0
  158. package/bin/mint/client/src/pages/api/name.ts +8 -0
  159. package/bin/mint/client/src/pages/api/request.ts +31 -0
  160. package/bin/mint/client/src/pages/api/suggest.ts +10 -0
  161. package/bin/mint/client/src/pages/api/syntax-highlighted-json.ts +13 -0
  162. package/bin/mint/client/src/pages/api/utils.ts +6 -0
  163. package/bin/mint/client/src/pages/index.tsx +31 -0
  164. package/bin/mint/client/src/ui/Api.tsx +359 -0
  165. package/bin/mint/client/src/ui/Footer.tsx +124 -0
  166. package/bin/mint/client/src/ui/Header.tsx +370 -0
  167. package/bin/mint/client/src/ui/Logo.tsx +55 -0
  168. package/bin/mint/client/src/ui/PageHeader.tsx +51 -0
  169. package/bin/mint/client/src/ui/Search.tsx +386 -0
  170. package/bin/mint/client/src/ui/ThemeToggle.tsx +285 -0
  171. package/bin/mint/client/src/ui/Title.tsx +22 -0
  172. package/bin/mint/client/src/ui/TopLevelLink.tsx +122 -0
  173. package/bin/mint/client/src/utils/api.ts +252 -0
  174. package/bin/mint/client/src/utils/brands.ts +217 -0
  175. package/bin/mint/client/src/utils/castArray.ts +3 -0
  176. package/bin/mint/client/src/utils/childrenArray.ts +3 -0
  177. package/bin/mint/client/src/utils/fit.ts +27 -0
  178. package/bin/mint/client/src/utils/fontAwesome.ts +577 -0
  179. package/bin/mint/client/src/utils/getAnalyticsConfig.ts +14 -0
  180. package/bin/mint/client/src/utils/getLogoHref.ts +9 -0
  181. package/bin/mint/client/src/utils/getOpenApiContext.ts +26 -0
  182. package/bin/mint/client/src/utils/importAll.ts +6 -0
  183. package/bin/mint/client/src/utils/isObject.ts +3 -0
  184. package/bin/mint/client/src/utils/kebabToTitleCase.ts +3 -0
  185. package/bin/mint/client/src/utils/loadImage.ts +8 -0
  186. package/bin/mint/client/src/utils/slugToTitle.ts +7 -0
  187. package/bin/mint/client/src/utils/wait.ts +5 -0
  188. package/bin/mint/client/tailwind.config.cjs +323 -0
  189. package/bin/mint/client/test/test.test.ts +5 -0
  190. package/bin/mint/client/tsconfig.json +36 -0
  191. package/bin/mint/client/yarn.lock +9702 -0
  192. package/bin/navigation.js +4 -0
  193. package/bin/navigation.js.map +1 -0
  194. package/bin/pageTemplate.js +30 -0
  195. package/bin/pageTemplate.js.map +1 -0
  196. package/bin/scraping/combineNavWithEmptyGroupTitles.js +20 -0
  197. package/bin/scraping/combineNavWithEmptyGroupTitles.js.map +1 -0
  198. package/bin/scraping/detectFramework.js +25 -0
  199. package/bin/scraping/detectFramework.js.map +1 -0
  200. package/bin/scraping/downloadAllImages.js +57 -0
  201. package/bin/scraping/downloadAllImages.js.map +1 -0
  202. package/bin/scraping/getSitemapLinks.js +18 -0
  203. package/bin/scraping/getSitemapLinks.js.map +1 -0
  204. package/bin/scraping/replaceImagePaths.js +17 -0
  205. package/bin/scraping/replaceImagePaths.js.map +1 -0
  206. package/bin/scraping/scrapeFileGettingFileNameFromUrl.js +43 -0
  207. package/bin/scraping/scrapeFileGettingFileNameFromUrl.js.map +1 -0
  208. package/bin/scraping/scrapeGettingFileNameFromUrl.js +14 -0
  209. package/bin/scraping/scrapeGettingFileNameFromUrl.js.map +1 -0
  210. package/bin/scraping/scrapePage.js +9 -0
  211. package/bin/scraping/scrapePage.js.map +1 -0
  212. package/bin/scraping/scrapePageCommands.js +48 -0
  213. package/bin/scraping/scrapePageCommands.js.map +1 -0
  214. package/bin/scraping/scrapeSection.js +9 -0
  215. package/bin/scraping/scrapeSection.js.map +1 -0
  216. package/bin/scraping/scrapeSectionCommands.js +90 -0
  217. package/bin/scraping/scrapeSectionCommands.js.map +1 -0
  218. package/bin/scraping/site-scrapers/getLinksRecursively.js +33 -0
  219. package/bin/scraping/site-scrapers/getLinksRecursively.js.map +1 -0
  220. package/bin/scraping/site-scrapers/scrapeDocusaurusPage.js +43 -0
  221. package/bin/scraping/site-scrapers/scrapeDocusaurusPage.js.map +1 -0
  222. package/bin/scraping/site-scrapers/scrapeDocusaurusSection.js +52 -0
  223. package/bin/scraping/site-scrapers/scrapeDocusaurusSection.js.map +1 -0
  224. package/bin/{scrapeGitBookPage.js → scraping/site-scrapers/scrapeGitBookPage.js} +10 -5
  225. package/bin/scraping/site-scrapers/scrapeGitBookPage.js.map +1 -0
  226. package/bin/scraping/site-scrapers/scrapeGitBookSection.js +51 -0
  227. package/bin/scraping/site-scrapers/scrapeGitBookSection.js.map +1 -0
  228. package/bin/scraping/site-scrapers/scrapeReadMePage.js +35 -0
  229. package/bin/scraping/site-scrapers/scrapeReadMePage.js.map +1 -0
  230. package/bin/scraping/site-scrapers/scrapeReadMeSection.js +38 -0
  231. package/bin/scraping/site-scrapers/scrapeReadMeSection.js.map +1 -0
  232. package/bin/util.js +47 -8
  233. package/bin/util.js.map +1 -1
  234. package/bin/validation/isValidLink.js +11 -0
  235. package/bin/validation/isValidLink.js.map +1 -0
  236. package/bin/validation/stopIfInvalidLink.js +9 -0
  237. package/bin/validation/stopIfInvalidLink.js.map +1 -0
  238. package/package.json +21 -4
  239. package/src/browser.ts +24 -0
  240. package/src/constants.ts +10 -0
  241. package/src/downloadImage.ts +35 -0
  242. package/src/index.ts +111 -122
  243. package/src/init-command/index.ts +59 -0
  244. package/src/{templates.ts → init-command/templates.ts} +0 -0
  245. package/src/local-preview/categorizeFiles.ts +74 -0
  246. package/src/local-preview/getOpenApiContext.ts +61 -0
  247. package/src/local-preview/index.ts +164 -0
  248. package/src/local-preview/injectFavicons.ts +76 -0
  249. package/src/local-preview/listener.ts +116 -0
  250. package/src/local-preview/metadata.ts +154 -0
  251. package/src/local-preview/mintConfigFile.ts +48 -0
  252. package/src/local-preview/openApiCheck.ts +19 -0
  253. package/src/local-preview/slugToTitle.ts +7 -0
  254. package/src/navigation.ts +12 -0
  255. package/src/pageTemplate.ts +32 -0
  256. package/src/scraping/combineNavWithEmptyGroupTitles.ts +21 -0
  257. package/src/scraping/detectFramework.ts +31 -0
  258. package/src/scraping/downloadAllImages.ts +79 -0
  259. package/src/scraping/getSitemapLinks.ts +18 -0
  260. package/src/scraping/replaceImagePaths.ts +21 -0
  261. package/src/scraping/scrapeFileGettingFileNameFromUrl.ts +81 -0
  262. package/src/scraping/scrapeGettingFileNameFromUrl.ts +50 -0
  263. package/src/scraping/scrapePage.ts +24 -0
  264. package/src/scraping/scrapePageCommands.ts +52 -0
  265. package/src/scraping/scrapeSection.ts +16 -0
  266. package/src/scraping/scrapeSectionCommands.ts +110 -0
  267. package/src/scraping/site-scrapers/getLinksRecursively.ts +40 -0
  268. package/src/scraping/site-scrapers/scrapeDocusaurusPage.ts +67 -0
  269. package/src/scraping/site-scrapers/scrapeDocusaurusSection.ts +80 -0
  270. package/src/{scrapeGitBookPage.ts → scraping/site-scrapers/scrapeGitBookPage.ts} +25 -5
  271. package/src/scraping/site-scrapers/scrapeGitBookSection.ts +77 -0
  272. package/src/scraping/site-scrapers/scrapeReadMePage.ts +57 -0
  273. package/src/scraping/site-scrapers/scrapeReadMeSection.ts +60 -0
  274. package/src/util.ts +53 -7
  275. package/src/validation/isValidLink.ts +9 -0
  276. package/src/validation/stopIfInvalidLink.ts +9 -0
  277. package/tsconfig.json +1 -1
  278. package/bin/scrapeGitBook.js +0 -28
  279. package/bin/scrapeGitBook.js.map +0 -1
  280. package/bin/scrapeGitBookPage.js.map +0 -1
  281. package/bin/scrapeReadMe.js +0 -60
  282. package/bin/scrapeReadMe.js.map +0 -1
  283. package/bin/scrapeReadMePage.js +0 -28
  284. package/bin/scrapeReadMePage.js.map +0 -1
  285. package/src/scrapeReadMe.ts +0 -79
  286. package/src/scrapeReadMePage.ts +0 -37
@@ -0,0 +1,301 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ import { Expandable } from '@/components/Expandable';
4
+ import { Heading } from '@/components/Heading';
5
+ import { ParamField } from '@/components/Param';
6
+ import { ResponseField } from '@/components/ResponseField';
7
+ import { config } from '@/config';
8
+ import { Component } from '@/enums/components';
9
+ import { openApi } from '@/openapi';
10
+ import { Api, APIBASE_CONFIG_STORAGE, ApiComponent } from '@/ui/Api';
11
+ import { MediaType } from '@/utils/api';
12
+ import { getOpenApiOperationMethodAndEndpoint } from '@/utils/getOpenApiContext';
13
+
14
+ type OpenApiContentProps = {
15
+ openapi: string;
16
+ auth?: string;
17
+ };
18
+
19
+ const getType = (schema: any) => {
20
+ if (schema.type === 'string' && schema.format === 'binary') {
21
+ return 'file';
22
+ }
23
+ return schema.type;
24
+ };
25
+
26
+ const getMedia = (name: string): MediaType => {
27
+ switch (name) {
28
+ case 'multipart/form-data':
29
+ return 'form';
30
+ default:
31
+ return 'json';
32
+ }
33
+ };
34
+
35
+ const getEnumDescription = (enumArray?: string[]): React.ReactNode | null => {
36
+ if (enumArray == null || enumArray.length === 0) {
37
+ return null;
38
+ }
39
+ return (
40
+ <>
41
+ Allowed values:{' '}
42
+ {enumArray.map((val, i) => (
43
+ <>
44
+ <code>{val}</code>
45
+ {i !== enumArray.length - 1 && ', '}
46
+ </>
47
+ ))}
48
+ </>
49
+ );
50
+ };
51
+
52
+ function ExpandableFields({ schema }: any) {
53
+ const [expandedFields, setExpandedFields] = useState<Record<string, boolean>>({});
54
+ if (schema == null) {
55
+ return null;
56
+ }
57
+
58
+ if (schema.items) {
59
+ const name = schema.items.title;
60
+ return (
61
+ <ResponseField name={name} type={schema.items.type}>
62
+ <>
63
+ {schema.description}
64
+ <Expandable
65
+ title="properties"
66
+ defaultOpen={false}
67
+ onChange={(open) => {
68
+ setExpandedFields({ ...expandedFields, [name]: open });
69
+ return;
70
+ }}
71
+ >
72
+ <ExpandableFields schema={schema.items} />
73
+ </Expandable>
74
+ </>
75
+ </ResponseField>
76
+ );
77
+ }
78
+
79
+ // TBD: Cleanup response field by types
80
+ return (
81
+ <>
82
+ {schema.properties &&
83
+ Object.entries(schema.properties)
84
+ ?.sort(([propertyLeft], [propertyRight]) => {
85
+ // Brings all required to the top
86
+ return schema.required?.includes(propertyLeft)
87
+ ? -1
88
+ : schema.required?.includes(propertyRight)
89
+ ? 1
90
+ : 0;
91
+ })
92
+ .map(([property, value]: any) => {
93
+ const isArrayExpandable = Boolean(value.items && value.items.properties == null);
94
+ const type =
95
+ isArrayExpandable && value.items.type ? `${value.items.type}[]` : value.type;
96
+ return (
97
+ <ResponseField
98
+ key={property}
99
+ name={property}
100
+ required={schema.required?.includes(property)}
101
+ type={type}
102
+ >
103
+ {/* Is array nested */}
104
+ {value.items && !isArrayExpandable ? (
105
+ <div className="mt-2">
106
+ {value.description}
107
+ <Expandable
108
+ title={value.items.type || 'properties'}
109
+ onChange={(open) => {
110
+ setExpandedFields({ ...expandedFields, [property]: open });
111
+ return;
112
+ }}
113
+ >
114
+ <ExpandableFields schema={value.items} />
115
+ </Expandable>
116
+ </div>
117
+ ) : (
118
+ <>
119
+ {value.description || value.title || getEnumDescription(value.enum)}
120
+ {value.properties && (
121
+ <div className="mt-2">
122
+ <Expandable
123
+ title={value.type || 'properties'}
124
+ onChange={(open) => {
125
+ setExpandedFields({ ...expandedFields, [property]: open });
126
+ return;
127
+ }}
128
+ >
129
+ <ExpandableFields schema={value}></ExpandableFields>
130
+ </Expandable>
131
+ </div>
132
+ )}
133
+ </>
134
+ )}
135
+ </ResponseField>
136
+ );
137
+ })}
138
+ </>
139
+ );
140
+ }
141
+
142
+ export function OpenApiContent({ openapi, auth }: OpenApiContentProps) {
143
+ const [apiBaseIndex, setApiBaseIndex] = useState(0);
144
+ const { method, endpoint, operation } = getOpenApiOperationMethodAndEndpoint(openapi);
145
+ useEffect(() => {
146
+ const configuredApiBaseIndex = window.localStorage.getItem(APIBASE_CONFIG_STORAGE);
147
+ if (configuredApiBaseIndex != null) {
148
+ setApiBaseIndex(parseInt(configuredApiBaseIndex, 10));
149
+ }
150
+ }, [openapi]);
151
+
152
+ if (operation == null) {
153
+ return null;
154
+ }
155
+
156
+ let apiComponents: ApiComponent[] = [];
157
+
158
+ const Parameters = operation.parameters?.map((parameter: any, i: number) => {
159
+ const { name, description, required, schema, in: paramType } = parameter;
160
+ const paramName = { [paramType]: name };
161
+ const type = getType(schema);
162
+ apiComponents.push({
163
+ type: Component.ParamField,
164
+ attributes: [
165
+ {
166
+ type: 'mdx',
167
+ name: paramType,
168
+ value: name,
169
+ },
170
+ {
171
+ type: 'mdx',
172
+ name: 'required',
173
+ value: required,
174
+ },
175
+ {
176
+ type: 'mdx',
177
+ name: 'type',
178
+ value: type,
179
+ },
180
+ {
181
+ type: 'mdx',
182
+ name: 'default',
183
+ value: schema.default,
184
+ },
185
+ {
186
+ type: 'mdx',
187
+ name: 'enum',
188
+ value: schema.enum,
189
+ },
190
+ ],
191
+ });
192
+ return (
193
+ <ParamField
194
+ key={i}
195
+ {...paramName}
196
+ required={required}
197
+ type={type}
198
+ default={schema.default}
199
+ enum={schema.enum}
200
+ >
201
+ {description || schema.description || schema.title}
202
+ </ParamField>
203
+ );
204
+ });
205
+
206
+ const bodyContent = operation.requestBody?.content;
207
+ const contentMedia = bodyContent && Object.keys(bodyContent)[0];
208
+ const bodySchema = bodyContent && bodyContent[contentMedia]?.schema;
209
+
210
+ const Body =
211
+ bodySchema?.properties &&
212
+ Object.entries(bodySchema.properties)?.map(([property, value]: any, i: number) => {
213
+ const required = bodySchema.required?.includes(property);
214
+ const type = getType(value);
215
+ const bodyDefault = bodySchema.example
216
+ ? JSON.stringify(bodySchema.example[property])
217
+ : undefined;
218
+ const last = i + 1 === operation.parameters?.length;
219
+ apiComponents.push({
220
+ type: Component.ParamField,
221
+ attributes: [
222
+ {
223
+ type: 'mdx',
224
+ name: 'body',
225
+ value: property,
226
+ },
227
+ {
228
+ type: 'mdx',
229
+ name: 'required',
230
+ value: required,
231
+ },
232
+ {
233
+ type: 'mdx',
234
+ name: 'type',
235
+ value: type,
236
+ },
237
+ {
238
+ type: 'mdx',
239
+ name: 'default',
240
+ value: bodyDefault,
241
+ },
242
+ {
243
+ type: 'mdx',
244
+ name: 'enum',
245
+ value: value.enum,
246
+ },
247
+ {
248
+ type: 'mdx',
249
+ name: 'last',
250
+ value: last,
251
+ },
252
+ ],
253
+ });
254
+ return (
255
+ <ParamField
256
+ body={property}
257
+ required={required}
258
+ type={type}
259
+ default={bodyDefault}
260
+ enum={value.enum}
261
+ last={last}
262
+ >
263
+ {value.description || value.title}
264
+ </ParamField>
265
+ );
266
+ });
267
+
268
+ let responseSchema = operation.responses?.['200']?.content?.['application/json']?.schema;
269
+ // endpoint in OpenAPI refers to the path
270
+ const configBaseUrl =
271
+ config.api?.baseUrl ?? openApi?.servers?.map((server: { url: string }) => server.url);
272
+ const baseUrl =
273
+ configBaseUrl && Array.isArray(configBaseUrl) ? configBaseUrl[apiBaseIndex] : configBaseUrl;
274
+ const api = `${method} ${baseUrl}${endpoint}`;
275
+
276
+ return (
277
+ <div className="prose prose-slate dark:prose-dark">
278
+ <Api api={api} media={getMedia(contentMedia)} auth={auth} apiComponents={apiComponents} />
279
+ <div>
280
+ {Parameters?.length > 0 && (
281
+ <Heading level={3} id="parameters" nextElement={null}>
282
+ Parameters
283
+ </Heading>
284
+ )}
285
+ {Parameters}
286
+ {Body?.length > 0 && (
287
+ <Heading level={3} id="body" nextElement={null}>
288
+ Body
289
+ </Heading>
290
+ )}
291
+ <ExpandableFields schema={bodySchema} />
292
+ {responseSchema && (
293
+ <Heading level={3} id="response" nextElement={null}>
294
+ Response
295
+ </Heading>
296
+ )}
297
+ <ExpandableFields schema={responseSchema} />
298
+ </div>
299
+ </div>
300
+ );
301
+ }
@@ -0,0 +1,412 @@
1
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
2
+ import { Dialog } from '@headlessui/react';
3
+ import clsx from 'clsx';
4
+ import isAbsoluteUrl from 'is-absolute-url';
5
+ import Link from 'next/link';
6
+ import { useRouter } from 'next/router';
7
+ import { ReactNode } from 'react';
8
+ import { createContext, forwardRef, useRef, useState } from 'react';
9
+
10
+ import { useIsomorphicLayoutEffect } from '@/hooks/useIsomorphicLayoutEffect';
11
+ import { getGroupsInDivision, getGroupsNotInDivision } from '@/layouts/getGroupsInDivision';
12
+ import { PageContext, Group, Groups, GroupPage, isGroup } from '@/metadata';
13
+ import { extractMethodAndEndpoint } from '@/utils/api';
14
+ import { getMethodDotsColor } from '@/utils/brands';
15
+
16
+ import { config, findFirstNavigationEntry } from '../config';
17
+ import { StyledTopLevelLink, TopLevelLink } from '../ui/TopLevelLink';
18
+ import isPathInGroupPages from './isPathInGroupPages';
19
+
20
+ type SidebarContextType = {
21
+ nav: any;
22
+ navIsOpen: boolean;
23
+ setNavIsOpen: (navIsOpen: boolean) => void;
24
+ };
25
+
26
+ // @ts-ignore
27
+ export const SidebarContext = createContext<SidebarContextType>({
28
+ nav: [],
29
+ navIsOpen: false,
30
+ setNavIsOpen: () => {},
31
+ });
32
+
33
+ const getPaddingByLevel = (level: number) => {
34
+ switch (level) {
35
+ case 0:
36
+ return 'pl-4';
37
+ case 1:
38
+ return 'pl-7';
39
+ default:
40
+ return 'pl-10';
41
+ }
42
+ };
43
+
44
+ const NavItem = forwardRef(
45
+ ({ groupPage, level = 0 }: { groupPage: GroupPage | undefined; level?: number }, ref: any) => {
46
+ const router = useRouter();
47
+
48
+ if (groupPage == null) {
49
+ return null;
50
+ }
51
+
52
+ if (isGroup(groupPage)) {
53
+ return <GroupDropdown group={groupPage} level={level} />;
54
+ }
55
+
56
+ const { href, api: pageApi, openapi } = groupPage;
57
+
58
+ const isActive = groupPage.href === router.pathname;
59
+ const api = pageApi || openapi;
60
+ const title = groupPage.sidebarTitle || groupPage.title;
61
+
62
+ return (
63
+ <li ref={ref}>
64
+ <Link href={href || '/'}>
65
+ <a
66
+ className={clsx(
67
+ 'flex border-l -ml-px',
68
+ isActive
69
+ ? 'text-primary border-current font-semibold dark:text-primary-light'
70
+ : 'border-transparent hover:border-slate-400 dark:hover:border-slate-500 text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300',
71
+ getPaddingByLevel(level),
72
+ config.classes?.navigationItem
73
+ )}
74
+ >
75
+ {api && (
76
+ <div
77
+ className={clsx('mt-[0.5rem] mr-2 h-2 w-2 rounded-sm', {
78
+ 'bg-primary dark:bg-primary-light': isActive,
79
+ [getMethodDotsColor(extractMethodAndEndpoint(api).method)]: !isActive,
80
+ })}
81
+ />
82
+ )}
83
+ <div className="flex-1">{title}</div>
84
+ </a>
85
+ </Link>
86
+ </li>
87
+ );
88
+ }
89
+ );
90
+
91
+ const GroupDropdown = ({ group, level }: { group: Group; level: number }) => {
92
+ const [isOpen, setIsOpen] = useState(false);
93
+ const router = useRouter();
94
+ const { group: name, pages } = group;
95
+
96
+ if (!name || !pages) {
97
+ return null;
98
+ }
99
+
100
+ const onClick = () => {
101
+ // Do not navigate if:
102
+ // 1. closing
103
+ // 2. The first link is another nested menu
104
+ // 3. The current page is in the nested pages being exposed
105
+ if (
106
+ !isOpen &&
107
+ !isGroup(pages[0]) &&
108
+ pages[0]?.href &&
109
+ !isPathInGroupPages(router.pathname, pages)
110
+ ) {
111
+ // Navigate to the first page if it exists
112
+ router.push(pages[0].href);
113
+ }
114
+ setIsOpen(!isOpen);
115
+ };
116
+
117
+ return (
118
+ <>
119
+ <span
120
+ className={clsx(
121
+ 'group flex items-center border-l -ml-px cursor-pointer space-x-3 border-transparent hover:border-slate-400 dark:hover:border-slate-500 text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300',
122
+ getPaddingByLevel(level)
123
+ )}
124
+ onClick={onClick}
125
+ >
126
+ <div>{name}</div>
127
+ <svg
128
+ width="3"
129
+ height="24"
130
+ viewBox="0 -9 3 24"
131
+ className={clsx(
132
+ 'text-slate-400 overflow-visible group-hover:text-slate-600 dark:text-slate-600 dark:group-hover:text-slate-500',
133
+ isOpen && 'rotate-90'
134
+ )}
135
+ >
136
+ <path
137
+ d="M0 0L3 3L0 6"
138
+ fill="none"
139
+ stroke="currentColor"
140
+ strokeWidth="1.5"
141
+ strokeLinecap="round"
142
+ ></path>
143
+ </svg>
144
+ </span>
145
+ {isOpen && pages.map((subpage) => <NavItem groupPage={subpage} level={level + 1} />)}
146
+ </>
147
+ );
148
+ };
149
+
150
+ function nearestScrollableContainer(el: any) {
151
+ function isScrollable(el: Element) {
152
+ const style = window.getComputedStyle(el);
153
+ const overflowX = style['overflowX'];
154
+ const overflowY = style['overflowY'];
155
+ const canScrollY = el.clientHeight < el.scrollHeight;
156
+ const canScrollX = el.clientWidth < el.scrollWidth;
157
+
158
+ const isScrollableY = canScrollY && (overflowY === 'auto' || overflowY === 'scroll');
159
+ const isScrollableX = canScrollX && (overflowX === 'auto' || overflowX === 'scroll');
160
+
161
+ return isScrollableY || isScrollableX;
162
+ }
163
+
164
+ while (el && el !== document.body && isScrollable(el) === false) {
165
+ el = el.parentNode || el.host;
166
+ }
167
+
168
+ return el;
169
+ }
170
+
171
+ function Nav({ nav, children, mobile = false }: any) {
172
+ const router = useRouter();
173
+ const activeItemRef: any = useRef();
174
+ const previousActiveItemRef: any = useRef();
175
+ const scrollRef: any = useRef();
176
+
177
+ let numPages = 0;
178
+ if (nav) {
179
+ nav.forEach((group: { group: string; pages: string[] }) => {
180
+ numPages += group.pages.length;
181
+ });
182
+ }
183
+
184
+ useIsomorphicLayoutEffect(() => {
185
+ function updatePreviousRef() {
186
+ previousActiveItemRef.current = activeItemRef.current;
187
+ }
188
+
189
+ if (activeItemRef.current) {
190
+ if (activeItemRef.current === previousActiveItemRef.current) {
191
+ updatePreviousRef();
192
+ return;
193
+ }
194
+
195
+ updatePreviousRef();
196
+
197
+ const scrollable = nearestScrollableContainer(scrollRef.current);
198
+
199
+ const scrollRect = scrollable.getBoundingClientRect();
200
+ const activeItemRect = activeItemRef.current.getBoundingClientRect();
201
+
202
+ const top = activeItemRef.current.offsetTop;
203
+ const bottom = top - scrollRect.height + activeItemRect.height;
204
+
205
+ if (scrollable.scrollTop > top || scrollable.scrollTop < bottom) {
206
+ scrollable.scrollTop = top - scrollRect.height / 2 + activeItemRect.height / 2;
207
+ }
208
+ }
209
+ }, [router.pathname]);
210
+
211
+ return (
212
+ <nav ref={scrollRef} id="nav" className="lg:text-sm lg:leading-6 relative">
213
+ <div className="sticky top-0 -ml-0.5 pointer-events-none">
214
+ {!mobile && (
215
+ <div
216
+ className={clsx(
217
+ 'h-8',
218
+ config.backgroundImage == null &&
219
+ 'bg-gradient-to-b from-background-light dark:from-background-dark'
220
+ )}
221
+ />
222
+ )}
223
+ </div>
224
+ <ul>
225
+ {config?.anchors != null && config.anchors.length > 0 && <TopLevelNav mobile={mobile} />}
226
+ {children}
227
+ {nav &&
228
+ numPages > 0 &&
229
+ nav
230
+ .map(({ group, pages }: { group: string; pages: PageContext[] }, i: number) => {
231
+ return (
232
+ <li
233
+ key={i}
234
+ className={clsx({
235
+ 'mt-12 lg:mt-8': !Boolean(
236
+ i === 0 && (config?.anchors == null || config.anchors?.length === 0)
237
+ ),
238
+ })}
239
+ >
240
+ <h5 className="mb-8 lg:mb-3 font-semibold text-slate-900 dark:text-slate-200">
241
+ {group}
242
+ </h5>
243
+ <ul
244
+ className={clsx(
245
+ 'space-y-6 lg:space-y-2 border-l border-slate-100',
246
+ mobile ? 'dark:border-slate-700' : 'dark:border-slate-800'
247
+ )}
248
+ >
249
+ {pages.map((page, i: number) => {
250
+ return <NavItem key={i} groupPage={page} />;
251
+ })}
252
+ </ul>
253
+ </li>
254
+ );
255
+ })
256
+ .filter(Boolean)}
257
+ </ul>
258
+ </nav>
259
+ );
260
+ }
261
+
262
+ function TopLevelNav({ mobile }: { mobile: boolean }) {
263
+ let { pathname } = useRouter();
264
+ const isRootAnchorActive =
265
+ pathname.startsWith('/') &&
266
+ !config.anchors?.some((anchor) => pathname.startsWith(`/${anchor.url}`));
267
+ return (
268
+ <>
269
+ <TopLevelLink
270
+ i={0}
271
+ mobile={mobile}
272
+ href="/"
273
+ isActive={isRootAnchorActive}
274
+ className="mb-4"
275
+ shadow="group-hover:shadow-primary-ultralight dark:group-hover:bg-primary"
276
+ icon={
277
+ <FontAwesomeIcon
278
+ className={clsx(
279
+ `h-6 w-6 p-1 text-white secondary-opacity group-hover:fill-primary-dark dark:group-hover:text-white`,
280
+ isRootAnchorActive ? 'dark:text-white' : 'dark:text-slate-500'
281
+ )}
282
+ icon={['fad', 'book-open']}
283
+ />
284
+ }
285
+ >
286
+ {config.topAnchor?.name ?? 'Documentation'}
287
+ </TopLevelLink>
288
+ {config?.anchors &&
289
+ config.anchors
290
+ .filter((anchor) => {
291
+ if (!anchor.isDefaultHidden) {
292
+ return true;
293
+ }
294
+
295
+ return pathname.startsWith(`/${anchor.url}`);
296
+ })
297
+ .map((anchor, i) => {
298
+ const isAbsolute = isAbsoluteUrl(anchor.url);
299
+ let href;
300
+ if (isAbsolute) {
301
+ href = anchor.url;
302
+ } else {
303
+ config.navigation?.every((nav) => {
304
+ const page = findFirstNavigationEntry(nav, `${anchor.url}/`);
305
+ if (page) {
306
+ if (typeof page === 'string') {
307
+ href = `/${page}`;
308
+ } else {
309
+ href = `/${page.pages[0]}`;
310
+ }
311
+ return false;
312
+ }
313
+ return true;
314
+ });
315
+ }
316
+
317
+ return (
318
+ <StyledTopLevelLink
319
+ i={i}
320
+ key={i}
321
+ mobile={mobile}
322
+ href={href || '/'}
323
+ name={anchor?.name}
324
+ icon={anchor?.icon}
325
+ color={anchor?.color}
326
+ isActive={pathname.startsWith(`/${anchor.url}`)}
327
+ />
328
+ );
329
+ })}
330
+ </>
331
+ );
332
+ }
333
+
334
+ function Wrapper({
335
+ allowOverflow,
336
+ children,
337
+ }: {
338
+ allowOverflow: boolean;
339
+ children: React.ReactChild;
340
+ }) {
341
+ return <div className={allowOverflow ? undefined : 'overflow-hidden'}>{children}</div>;
342
+ }
343
+
344
+ // TODO: Set remaining types
345
+ export function SidebarLayout({
346
+ navIsOpen,
347
+ setNavIsOpen,
348
+ nav,
349
+ layoutProps: { allowOverflow = true } = {},
350
+ children,
351
+ }: {
352
+ navIsOpen: boolean;
353
+ setNavIsOpen: any;
354
+ nav: Groups;
355
+ layoutProps?: any;
356
+ children: ReactNode;
357
+ }) {
358
+ const router = useRouter();
359
+ const pathname = router.pathname;
360
+ const currentDivision = config.anchors?.find((anchor) => pathname.startsWith(`/${anchor.url}`));
361
+
362
+ let navForDivision = getGroupsInDivision(nav, currentDivision?.url ? [currentDivision?.url] : []);
363
+
364
+ if (navForDivision.length === 0) {
365
+ // Base docs include everything NOT in an anchor
366
+ const divisions = config.anchors?.filter((anchor) => !isAbsoluteUrl(anchor.url)) || [];
367
+ navForDivision = getGroupsNotInDivision(
368
+ nav,
369
+ divisions.map((division) => division.url)
370
+ );
371
+ }
372
+
373
+ return (
374
+ <SidebarContext.Provider value={{ nav, navIsOpen, setNavIsOpen }}>
375
+ <Wrapper allowOverflow={allowOverflow}>
376
+ <div className="max-w-8xl mx-auto px-4 sm:px-6 md:px-8">
377
+ <div className="hidden lg:block fixed z-20 top-[3.8125rem] bottom-0 left-[max(0px,calc(50%-45rem))] right-auto w-[19.5rem] pb-10 px-8 overflow-y-auto">
378
+ <Nav nav={navForDivision} />
379
+ </div>
380
+ <div className="lg:pl-[20rem]">{children}</div>
381
+ </div>
382
+ </Wrapper>
383
+ <Dialog
384
+ as="div"
385
+ open={navIsOpen}
386
+ onClose={() => setNavIsOpen(false)}
387
+ className="fixed z-50 inset-0 overflow-y-auto lg:hidden"
388
+ >
389
+ <Dialog.Overlay className="fixed inset-0 bg-black/20 backdrop-blur-sm dark:bg-slate-900/80" />
390
+ <div className="relative bg-white w-80 min-h-full max-w-[calc(100%-3rem)] p-6 dark:bg-slate-800">
391
+ <button
392
+ type="button"
393
+ onClick={() => setNavIsOpen(false)}
394
+ className="absolute z-10 top-5 right-5 w-8 h-8 flex items-center justify-center text-slate-500 hover:text-slate-600 dark:text-slate-400 dark:hover:text-slate-300"
395
+ >
396
+ <span className="sr-only">Close navigation</span>
397
+ <svg viewBox="0 0 10 10" className="w-2.5 h-2.5 overflow-visible">
398
+ <path
399
+ d="M0 0L10 10M10 0L0 10"
400
+ fill="none"
401
+ stroke="currentColor"
402
+ strokeWidth="2"
403
+ strokeLinecap="round"
404
+ />
405
+ </svg>
406
+ </button>
407
+ <Nav nav={navForDivision} mobile={true} />
408
+ </div>
409
+ </Dialog>
410
+ </SidebarContext.Provider>
411
+ );
412
+ }