zudoku 0.3.0-dev.11 → 0.3.0-dev.13

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 (85) hide show
  1. package/dist/app/App.js +9 -7
  2. package/dist/app/App.js.map +1 -1
  3. package/dist/config/config.d.ts +5 -14
  4. package/dist/lib/components/DevPortal.d.ts +2 -20
  5. package/dist/lib/components/DevPortal.js +1 -1
  6. package/dist/lib/components/DevPortal.js.map +1 -1
  7. package/dist/lib/components/Header.js +2 -2
  8. package/dist/lib/components/Header.js.map +1 -1
  9. package/dist/lib/components/Heading.d.ts +9 -4
  10. package/dist/lib/components/Heading.js +17 -2
  11. package/dist/lib/components/Heading.js.map +1 -1
  12. package/dist/lib/components/Layout.js +1 -1
  13. package/dist/lib/components/Layout.js.map +1 -1
  14. package/dist/lib/core/DevPortalContext.d.ts +33 -3
  15. package/dist/lib/core/DevPortalContext.js +3 -1
  16. package/dist/lib/core/DevPortalContext.js.map +1 -1
  17. package/dist/lib/plugins/markdown/MdxPage.d.ts +3 -2
  18. package/dist/lib/plugins/markdown/MdxPage.js +5 -4
  19. package/dist/lib/plugins/markdown/MdxPage.js.map +1 -1
  20. package/dist/lib/plugins/markdown/generateRoutes.d.ts +2 -2
  21. package/dist/lib/plugins/markdown/generateRoutes.js +2 -2
  22. package/dist/lib/plugins/markdown/generateRoutes.js.map +1 -1
  23. package/dist/lib/plugins/markdown/index.d.ts +4 -1
  24. package/dist/lib/plugins/markdown/index.js +2 -2
  25. package/dist/lib/plugins/markdown/index.js.map +1 -1
  26. package/dist/lib/plugins/openapi/OperationList.js +4 -3
  27. package/dist/lib/plugins/openapi/OperationList.js.map +1 -1
  28. package/dist/lib/plugins/openapi/OperationListItem.js +8 -4
  29. package/dist/lib/plugins/openapi/OperationListItem.js.map +1 -1
  30. package/dist/lib/plugins/openapi/ParameterList.js +1 -1
  31. package/dist/lib/plugins/openapi/ParameterList.js.map +1 -1
  32. package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
  33. package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
  34. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js +1 -9
  35. package/dist/lib/plugins/openapi/RequestBodySidecarBox.js.map +1 -1
  36. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js +1 -1
  37. package/dist/lib/plugins/openapi/ResponsesSidecarBox.js.map +1 -1
  38. package/dist/lib/plugins/openapi/SchemaListView.js +4 -26
  39. package/dist/lib/plugins/openapi/SchemaListView.js.map +1 -1
  40. package/dist/lib/plugins/openapi/SchemaListViewItem.d.ts +7 -0
  41. package/dist/lib/plugins/openapi/SchemaListViewItem.js +16 -0
  42. package/dist/lib/plugins/openapi/SchemaListViewItem.js.map +1 -0
  43. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.d.ts +8 -0
  44. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js +17 -0
  45. package/dist/lib/plugins/openapi/SchemaListViewItemGroup.js.map +1 -0
  46. package/dist/lib/plugins/openapi/util/prose.d.ts +1 -0
  47. package/dist/lib/plugins/openapi/util/prose.js +4 -0
  48. package/dist/lib/plugins/openapi/util/prose.js.map +1 -0
  49. package/dist/lib/ui/button-variants.d.ts +1 -1
  50. package/dist/lib/util/MdxComponents.js +1 -1
  51. package/dist/lib/util/MdxComponents.js.map +1 -1
  52. package/dist/lib/util/objectEntries.d.ts +4 -0
  53. package/dist/lib/util/objectEntries.js +2 -0
  54. package/dist/lib/util/objectEntries.js.map +1 -0
  55. package/dist/lib/util/renderIf.d.ts +1 -0
  56. package/dist/lib/util/renderIf.js +2 -0
  57. package/dist/lib/util/renderIf.js.map +1 -0
  58. package/dist/vite/plugin-docs.js +1 -1
  59. package/dist/vite/plugin-docs.js.map +1 -1
  60. package/lib/{Spinner-CzCKCaUK.js → Spinner-CbtkR-Im.js} +3054 -3040
  61. package/lib/zudoku.components.js +42 -41
  62. package/lib/zudoku.plugins.js +2982 -2980
  63. package/package.json +1 -1
  64. package/src/app/App.tsx +10 -7
  65. package/src/lib/components/DevPortal.tsx +4 -25
  66. package/src/lib/components/Header.tsx +4 -4
  67. package/src/lib/components/Heading.tsx +26 -7
  68. package/src/lib/components/Layout.tsx +1 -1
  69. package/src/lib/core/DevPortalContext.ts +37 -9
  70. package/src/lib/plugins/markdown/MdxPage.tsx +49 -32
  71. package/src/lib/plugins/markdown/generateRoutes.tsx +12 -2
  72. package/src/lib/plugins/markdown/index.tsx +8 -1
  73. package/src/lib/plugins/openapi/OperationList.tsx +9 -3
  74. package/src/lib/plugins/openapi/OperationListItem.tsx +39 -18
  75. package/src/lib/plugins/openapi/ParameterList.tsx +1 -1
  76. package/src/lib/plugins/openapi/ParameterListItem.tsx +3 -4
  77. package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +2 -17
  78. package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +3 -1
  79. package/src/lib/plugins/openapi/SchemaListView.tsx +15 -182
  80. package/src/lib/plugins/openapi/SchemaListViewItem.tsx +110 -0
  81. package/src/lib/plugins/openapi/SchemaListViewItemGroup.tsx +63 -0
  82. package/src/lib/plugins/openapi/util/prose.ts +7 -0
  83. package/src/lib/util/MdxComponents.tsx +1 -1
  84. package/src/lib/util/objectEntries.ts +5 -0
  85. package/src/lib/util/renderIf.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zudoku",
3
- "version": "0.3.0-dev.11",
3
+ "version": "0.3.0-dev.13",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
package/src/app/App.tsx CHANGED
@@ -19,16 +19,19 @@ import { DevPortal } from "zudoku/components";
19
19
  export default function App() {
20
20
  return (
21
21
  <DevPortal
22
- meta={{
23
- headerTitle: config.ui?.headerTitle ?? "Developer Portal",
24
- pageTitle: config.ui?.pageTitle ?? "%s | Dev Portal",
25
- logo: config.ui?.logo ?? "https://cdn.zuplo.com/www/favicon.png",
26
- favicon:
27
- config.ui?.metadata?.favicon ??
28
- "https://cdn.zuplo.com/www/favicon.png",
22
+ page={{
23
+ logo: config.page?.logo ?? "https://cdn.zuplo.com/www/favicon.png",
24
+ pageTitle: "Developer Portal",
25
+ ...config.page,
26
+ }}
27
+ metadata={{
28
+ favicon: "https://cdn.zuplo.com/www/favicon.png",
29
+ title: "%s | Developer Portal",
30
+ ...config.metadata,
29
31
  }}
30
32
  navigation={config.navigation ?? []}
31
33
  authentication={configuredAuthProvider}
34
+ mdxComponents={config.mdx?.components}
32
35
  plugins={[
33
36
  ...configuredDocsPlugins,
34
37
  ...configuredApiPlugins,
@@ -4,20 +4,14 @@ import { Fragment, memo, Suspense, useEffect, useMemo } from "react";
4
4
  import {
5
5
  DevPortalContext,
6
6
  queryClient,
7
- type NavigationItem,
7
+ ZudokuContextOptions,
8
8
  } from "../core/DevPortalContext.js";
9
9
  import { Helmet, HelmetProvider } from "../core/helmet.js";
10
- import { hasHead, type DevPortalPlugin } from "../core/plugins.js";
11
-
12
- import { AuthenticationProvider } from "../authentication/authentication.js";
13
- import {
14
- MdxComponents,
15
- type MdxComponentsType,
16
- } from "../util/MdxComponents.js";
10
+ import { hasHead } from "../core/plugins.js";
11
+ import { MdxComponents } from "../util/MdxComponents.js";
17
12
  import {
18
13
  ComponentsProvider,
19
14
  DEFAULT_COMPONENTS,
20
- type ComponentsContextType,
21
15
  } from "./context/ComponentsContext.js";
22
16
  import { DevPortalProvider } from "./context/DevPortalProvider.js";
23
17
  import { ThemeProvider } from "./context/ThemeContext.js";
@@ -32,22 +26,7 @@ export type DevPortalPath =
32
26
  | string
33
27
  | (typeof DevPortalSystemPaths)[keyof typeof DevPortalSystemPaths];
34
28
 
35
- export type DevPortalProps = {
36
- meta?: Partial<{
37
- headerTitle: string;
38
- pageTitle: string;
39
- description: string;
40
- logo: string;
41
- favicon: string;
42
- }>;
43
- authentication?: AuthenticationProvider;
44
- navigation: NavigationItem[];
45
- plugins?: DevPortalPlugin[];
46
- mdxComponents?: MdxComponentsType;
47
- overrides?: ComponentsContextType;
48
- };
49
-
50
- const DevPortalInner = (props: DevPortalProps) => {
29
+ const DevPortalInner = (props: ZudokuContextOptions) => {
51
30
  const components = useMemo(
52
31
  () => ({ ...DEFAULT_COMPONENTS, ...props.overrides }),
53
32
  [props.overrides],
@@ -9,7 +9,7 @@ import { useTheme } from "./context/ThemeContext.js";
9
9
  export const Header = memo(function HeaderInner() {
10
10
  const [isDark, toggleTheme] = useTheme();
11
11
  const { isAuthenticated, profile, isAuthEnabled, login, logout } = useAuth();
12
- const { meta } = useDevPortal();
12
+ const { page } = useDevPortal();
13
13
 
14
14
  const ThemeIcon = isDark ? MoonStarIcon : SunIcon;
15
15
 
@@ -18,11 +18,11 @@ export const Header = memo(function HeaderInner() {
18
18
  <div className="max-w-screen-2xl mx-auto">
19
19
  <div className="grid grid-cols-[calc(var(--side-nav-width))_1fr] lg:gap-12 items-center border-b border-border px-12 h-[--top-header-height]">
20
20
  <div className="flex items-center gap-3.5">
21
- {meta?.logo && (
22
- <img src={meta.logo} alt="My Dev Portal" className="h-10" />
21
+ {page?.logo && (
22
+ <img src={page.logo} alt={page.pageTitle} className="h-10" />
23
23
  )}
24
24
  <span className="font-bold text-2xl text-foreground/85 tracking-wide">
25
- {meta?.headerTitle}
25
+ {page?.pageTitle}
26
26
  </span>
27
27
  </div>
28
28
  <div className="grid grid-cols-[--sidecar-grid-cols] items-center gap-8">
@@ -1,7 +1,24 @@
1
- import { type ReactNode } from "react";
2
- import { cn } from "../util/cn.js";
1
+ import React, { type ReactNode } from "react";
3
2
  import { useRegisterAnchorElement } from "./context/ViewportAnchorContext.js";
4
3
 
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+
6
+ const heading = cva("group relative", {
7
+ variants: {
8
+ level: {
9
+ 6: "text-md",
10
+ 5: "text-lg",
11
+ 4: "text-xl",
12
+ 3: "text-xl font-semibold",
13
+ 2: "text-2xl font-bold",
14
+ 1: "text-4xl font-extrabold",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ level: 1,
19
+ },
20
+ });
21
+
5
22
  const getComponent = (level: number) => {
6
23
  switch (level) {
7
24
  case 1:
@@ -21,27 +38,29 @@ const getComponent = (level: number) => {
21
38
  }
22
39
  };
23
40
 
24
- export type HeadingProps = {
41
+ export interface HeadingProps
42
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
43
+ VariantProps<typeof heading> {
25
44
  children: ReactNode;
26
45
  className?: string;
27
46
  id?: string;
28
47
  level?: 1 | 2 | 3 | 4 | 5 | 6;
29
48
  registerSidebarAnchor?: boolean;
30
- };
49
+ }
31
50
 
32
- export const Heading = ({
51
+ export const Heading: React.FC<HeadingProps> = ({
33
52
  level,
34
53
  children,
35
54
  id,
36
55
  className,
37
56
  registerSidebarAnchor,
38
- }: HeadingProps) => {
57
+ }) => {
39
58
  const Component = getComponent(level ?? 1);
40
59
  const { ref } = useRegisterAnchorElement();
41
60
 
42
61
  return (
43
62
  <Component
44
- className={cn("group relative", className)}
63
+ className={heading({ className, level })}
45
64
  ref={registerSidebarAnchor ? ref : undefined}
46
65
  id={id}
47
66
  >
@@ -29,7 +29,7 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
29
29
 
30
30
  return (
31
31
  <>
32
- <Helmet titleTemplate={meta?.pageTitle}>
32
+ <Helmet titleTemplate={meta?.title}>
33
33
  <title>Home</title>
34
34
  {meta?.description && (
35
35
  <meta name="description" content={meta.description} />
@@ -2,16 +2,15 @@ import { QueryClient } from "@tanstack/react-query";
2
2
  import { type ReactNode } from "react";
3
3
  import { create } from "zustand";
4
4
  import { type AuthenticationProvider } from "../authentication/authentication.js";
5
+ import type { ComponentsContextType } from "../components/context/ComponentsContext.js";
6
+ import { type DevPortalPath } from "../components/DevPortal.js";
7
+ import type { MdxComponentsType } from "../util/MdxComponents.js";
5
8
  import {
6
- type DevPortalPath,
7
- type DevPortalProps,
8
- } from "../components/DevPortal.js";
9
- import {
9
+ type DevPortalPlugin,
10
10
  isApiIdentityPlugin,
11
11
  isNavigationPlugin,
12
- needsInitialization,
13
- type DevPortalPlugin,
14
12
  type NavigationPlugin,
13
+ needsInitialization,
15
14
  } from "./plugins.js";
16
15
 
17
16
  export interface ApiIdentity {
@@ -66,21 +65,50 @@ export const useRoutingState = create<RoutingState>(() => ({}));
66
65
  export type ApiKeyCache = "api-keys";
67
66
  export type DevPortalCacheKey = ApiKeyCache | string;
68
67
 
68
+ export type ZudokuContextMetadataOptions = Partial<{
69
+ title: string;
70
+ description: string;
71
+ logo: string;
72
+ favicon: string;
73
+ generator: string;
74
+ applicationName: string;
75
+ referrer: string;
76
+ keywords: string[];
77
+ authors: string[];
78
+ creator: string;
79
+ publisher: string;
80
+ }>;
81
+ export type ZudokuContextPageOptions = Partial<{
82
+ pageTitle?: string;
83
+ logo?: string;
84
+ }>;
85
+ export type ZudokuContextOptions = {
86
+ metadata?: ZudokuContextMetadataOptions;
87
+ page?: ZudokuContextPageOptions;
88
+ authentication?: AuthenticationProvider;
89
+ navigation: NavigationItem[];
90
+ plugins?: DevPortalPlugin[];
91
+ mdxComponents?: MdxComponentsType;
92
+ overrides?: ComponentsContextType;
93
+ };
94
+
69
95
  export class DevPortalContext {
70
96
  private plugins: DevPortalPlugin[] = [];
71
97
  private navigationPlugins: NavigationPlugin[];
72
98
 
73
99
  public navigation: NavigationItem[];
74
- public meta: DevPortalProps["meta"];
100
+ public meta: ZudokuContextOptions["metadata"];
101
+ public page: ZudokuContextOptions["page"];
75
102
  public authentication?: AuthenticationProvider;
76
103
  public state: typeof useRoutingState;
77
104
 
78
- constructor(config: DevPortalProps) {
105
+ constructor(config: ZudokuContextOptions) {
79
106
  this.plugins = config.plugins ?? [];
80
107
  this.navigation = config.navigation;
81
108
  this.navigationPlugins = this.plugins.filter(isNavigationPlugin);
82
109
  this.authentication = config.authentication;
83
- this.meta = config.meta;
110
+ this.meta = config.metadata;
111
+ this.page = config.page;
84
112
  this.state = useRoutingState;
85
113
  }
86
114
 
@@ -12,7 +12,7 @@ import { cn } from "../../util/cn.js";
12
12
  import slugify from "../../util/slugify.js";
13
13
  import { traverseNavigation } from "../../util/traverseNavigation.js";
14
14
  import { Toc } from "./Toc.js";
15
- import type { MDXImport } from "./index.js";
15
+ import { MarkdownPluginDefaultOptions, MDXImport } from "./index.js";
16
16
 
17
17
  const MarkdownHeadings = {
18
18
  h2: ({ children, id }) => (
@@ -31,10 +31,12 @@ const MarkdownHeadings = {
31
31
  export const MdxPage = ({
32
32
  mdxComponent: MdxComponent,
33
33
  frontmatter = {},
34
+ defaultOptions,
34
35
  tableOfContents,
35
36
  }: PropsWithChildren<
36
37
  Omit<MDXImport, "default"> & {
37
38
  mdxComponent: MDXImport["default"];
39
+ defaultOptions?: MarkdownPluginDefaultOptions;
38
40
  }
39
41
  >) => {
40
42
  const navItem = useTopNavigationItem();
@@ -50,10 +52,11 @@ export const MdxPage = ({
50
52
 
51
53
  const title = frontmatter.title;
52
54
  const category = frontmatter.category ?? categoryTitle;
53
- const hideToc = frontmatter.toc === false;
54
-
55
+ const hideToc = frontmatter.toc === false || defaultOptions?.toc === false;
55
56
  const pageTitle =
56
57
  tableOfContents.find((item) => item.depth === 1)?.value ?? title;
58
+ const hidePager =
59
+ frontmatter.disablePager ?? defaultOptions?.disablePager ?? false;
57
60
 
58
61
  const tocEntries =
59
62
  tableOfContents.find((item) => item.depth === 1)?.children ??
@@ -112,35 +115,49 @@ export const MdxPage = ({
112
115
  <MdxComponent
113
116
  components={{ ...useMDXComponents(), ...MarkdownHeadings }}
114
117
  />
115
- <hr />
116
- <div className="not-prose flex items-center justify-between gap-8">
117
- {prev.path ? (
118
- <Link
119
- to={prev.path}
120
- className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-start hover:border-primary/85 transition shadow-sm hover:shadow-md"
121
- title={typeof prev.label === "string" ? prev.label : undefined}
122
- >
123
- <div className="text-sm text-muted-foreground">
124
- Previous page
125
- </div>
126
- <div className="text-lg text-primary truncate">{prev.label}</div>
127
- </Link>
128
- ) : (
129
- <div className="flex-1" />
130
- )}
131
- {next.path ? (
132
- <Link
133
- to={next.path}
134
- className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-end hover:border-primary/85 transition shadow-sm hover:shadow-md"
135
- title={typeof next.label === "string" ? next.label : undefined}
136
- >
137
- <div className="text-sm text-muted-foreground">Next page →</div>
138
- <div className="text-lg text-primary truncate">{next.label}</div>
139
- </Link>
140
- ) : (
141
- <div className="flex-1" />
142
- )}
143
- </div>
118
+ {!hidePager && (
119
+ <>
120
+ <hr />
121
+ <div className="not-prose flex items-center justify-between gap-8">
122
+ {prev.path ? (
123
+ <Link
124
+ to={prev.path}
125
+ className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-start hover:border-primary/85 transition shadow-sm hover:shadow-md"
126
+ title={
127
+ typeof prev.label === "string" ? prev.label : undefined
128
+ }
129
+ >
130
+ <div className="text-sm text-muted-foreground">
131
+ Previous page
132
+ </div>
133
+ <div className="text-lg text-primary truncate">
134
+ {prev.label}
135
+ </div>
136
+ </Link>
137
+ ) : (
138
+ <div className="flex-1" />
139
+ )}
140
+ {next.path ? (
141
+ <Link
142
+ to={next.path}
143
+ className="flex flex-col items-stretch gap-2 flex-1 truncate border border-border rounded px-6 py-4 text-end hover:border-primary/85 transition shadow-sm hover:shadow-md"
144
+ title={
145
+ typeof next.label === "string" ? next.label : undefined
146
+ }
147
+ >
148
+ <div className="text-sm text-muted-foreground">
149
+ Next page →
150
+ </div>
151
+ <div className="text-lg text-primary truncate">
152
+ {next.label}
153
+ </div>
154
+ </Link>
155
+ ) : (
156
+ <div className="flex-1" />
157
+ )}
158
+ </div>
159
+ </>
160
+ )}
144
161
  </div>
145
162
  <div className="hidden xl:block">
146
163
  {showToc && <Toc entries={tocEntries} />}
@@ -3,10 +3,14 @@ import { isPathItem } from "../../components/navigation/util.js";
3
3
  import { Navigate, type RouteObject } from "../../core/router.js";
4
4
  import { traverseNavigation } from "../../util/traverseNavigation.js";
5
5
  import { MdxPage } from "./MdxPage.js";
6
- import type { MarkdownPluginOptions } from "./index.js";
6
+ import {
7
+ MarkdownPluginDefaultOptions,
8
+ MarkdownPluginOptions,
9
+ } from "./index.js";
7
10
 
8
11
  export const generateRoutes = (
9
12
  markdownFiles: MarkdownPluginOptions["markdownFiles"],
13
+ defaultOptions?: MarkdownPluginDefaultOptions,
10
14
  ): RouteObject[] => {
11
15
  const routes = Object.entries(markdownFiles).flatMap(
12
16
  ([file, importPromise]) => {
@@ -28,7 +32,13 @@ export const generateRoutes = (
28
32
  const { default: Component, ...props } = await importPromise();
29
33
 
30
34
  return {
31
- element: <MdxPage mdxComponent={Component} {...props} />,
35
+ element: (
36
+ <MdxPage
37
+ mdxComponent={Component}
38
+ {...props}
39
+ defaultOptions={defaultOptions}
40
+ />
41
+ ),
32
42
  };
33
43
  },
34
44
  } satisfies RouteObject;
@@ -5,13 +5,19 @@ import { generateRoutes } from "./generateRoutes.js";
5
5
 
6
6
  export type MarkdownPluginOptions = {
7
7
  markdownFiles: Record<string, () => Promise<MDXImport>>;
8
+ defaultOptions?: MarkdownPluginDefaultOptions;
8
9
  };
10
+ export type MarkdownPluginDefaultOptions = Pick<
11
+ Frontmatter,
12
+ "toc" | "disablePager"
13
+ >;
9
14
 
10
15
  export type Frontmatter = {
11
16
  title?: string;
12
17
  description?: string;
13
18
  category?: string;
14
19
  toc?: boolean;
20
+ disablePager?: boolean;
15
21
  };
16
22
 
17
23
  export type MDXImport = {
@@ -22,10 +28,11 @@ export type MDXImport = {
22
28
 
23
29
  export const markdownPlugin = ({
24
30
  markdownFiles,
31
+ defaultOptions,
25
32
  }: MarkdownPluginOptions): DevPortalPlugin => {
26
33
  return {
27
34
  getRoutes() {
28
- return generateRoutes(markdownFiles);
35
+ return generateRoutes(markdownFiles, defaultOptions);
29
36
  },
30
37
  };
31
38
  };
@@ -1,11 +1,12 @@
1
1
  import { ResultOf } from "@graphql-typed-document-node/core";
2
2
  import { CategoryHeading } from "../../components/CategoryHeading.js";
3
3
  import { Heading } from "../../components/Heading.js";
4
- import { Markdown, ProseClasses } from "../../components/Markdown.js";
4
+ import { Markdown } from "../../components/Markdown.js";
5
5
  import { cn } from "../../util/cn.js";
6
6
  import { OperationListItem } from "./OperationListItem.js";
7
7
  import { graphql } from "./graphql/index.js";
8
8
  import { useOasConfig } from "./index.js";
9
+ import { SchemaProseClasses } from "./util/prose.js";
9
10
  import { useQuery } from "./util/urql.js";
10
11
 
11
12
  export const OperationsFragment = graphql(/* GraphQL */ `
@@ -94,7 +95,12 @@ export const OperationList = () => {
94
95
 
95
96
  return (
96
97
  <div className="pt-[--padding-content-top]">
97
- <div className={cn(ProseClasses, "mb-16")}>
98
+ <div
99
+ className={cn(
100
+ SchemaProseClasses,
101
+ "mb-16 max-w-full prose-img:max-w-prose",
102
+ )}
103
+ >
98
104
  <CategoryHeading>Overview</CategoryHeading>
99
105
  <Heading level={1} id="description" registerSidebarAnchor>
100
106
  {result.data.schema.title}
@@ -108,7 +114,7 @@ export const OperationList = () => {
108
114
  {tag.name && <CategoryHeading>{tag.name}</CategoryHeading>}
109
115
  {tag.description && (
110
116
  <Markdown
111
- className={`${ProseClasses} mt-2 mb-12`}
117
+ className={`${SchemaProseClasses} mt-2 mb-12`}
112
118
  content={tag.description}
113
119
  />
114
120
  )}
@@ -1,11 +1,13 @@
1
1
  import { Heading } from "../../components/Heading.js";
2
- import { Markdown, ProseClasses } from "../../components/Markdown.js";
2
+ import { Markdown } from "../../components/Markdown.js";
3
3
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/Tabs.js";
4
+ import { renderIf } from "../../util/renderIf.js";
4
5
  import { OperationsFragment } from "./OperationList.js";
5
6
  import { ParameterList } from "./ParameterList.js";
6
7
  import { SchemaListView } from "./SchemaListView.js";
7
8
  import { Sidecar } from "./Sidecar.js";
8
9
  import { FragmentType, useFragment } from "./graphql/index.js";
10
+ import { SchemaProseClasses } from "./util/prose.js";
9
11
 
10
12
  export const PARAM_GROUPS = ["path", "query", "header", "cookie"] as const;
11
13
  export type ParameterGroup = (typeof PARAM_GROUPS)[number];
@@ -20,24 +22,24 @@ export const OperationListItem = ({
20
22
  operation.parameters ?? [],
21
23
  (param) => param.in,
22
24
  );
23
- const first = operation.responses.find((re) => re.statusCode === "200");
25
+ const first = operation.responses.at(0);
24
26
  return (
25
27
  <div
26
28
  key={operation.operationId}
27
- className="grid grid-cols-2 gap-8 items-start border-b-2 mb-16 pb-16 border-border"
29
+ className="grid grid-cols-[4fr_3fr] gap-8 items-start border-b-2 mb-16 pb-16 border-border"
28
30
  >
29
- <div className={ProseClasses}>
30
- <Heading
31
- level={2}
32
- className="mt-0"
33
- id={operation.slug}
34
- registerSidebarAnchor
35
- >
31
+ <div className="flex flex-col gap-4">
32
+ <Heading level={2} id={operation.slug} registerSidebarAnchor>
36
33
  {operation.summary}
37
34
  </Heading>
38
- {operation.description && <Markdown content={operation.description} />}
35
+ {operation.description && (
36
+ <Markdown
37
+ className={SchemaProseClasses}
38
+ content={operation.description}
39
+ />
40
+ )}
39
41
  {operation.parameters && operation.parameters.length > 0 && (
40
- <div className="mt-4">
42
+ <>
41
43
  {PARAM_GROUPS.flatMap((group) =>
42
44
  groupedParameters[group]?.length ? (
43
45
  <ParameterList
@@ -50,11 +52,22 @@ export const OperationListItem = ({
50
52
  []
51
53
  ),
52
54
  )}
53
- </div>
55
+ </>
54
56
  )}
57
+ {renderIf(operation.requestBody?.content?.at(0)?.schema, (schema) => (
58
+ <div className="mt-4 flex flex-col gap-4">
59
+ <Heading level={3} className="capitalize">
60
+ Request Body
61
+ </Heading>
62
+ <SchemaListView schema={schema} />
63
+ </div>
64
+ ))}
55
65
  {operation.responses.length > 0 && (
56
66
  <>
57
- <Heading level={3} className="capitalize">
67
+ <Heading
68
+ level={3}
69
+ className="capitalize mt-8 pt-8 border-border border-t"
70
+ >
58
71
  Responses
59
72
  </Heading>
60
73
  <Tabs defaultValue={`${first?.statusCode}${first?.description}`}>
@@ -75,10 +88,18 @@ export const OperationListItem = ({
75
88
  value={response.statusCode + response.description}
76
89
  key={response.statusCode}
77
90
  >
78
- <SchemaListView
79
- schema={response.content?.at(0)?.schema}
80
- name=""
81
- />
91
+ {renderIf(
92
+ response.content?.find((content) => content.schema),
93
+ (content) => {
94
+ return (
95
+ <SchemaListView schema={content.schema} name="" />
96
+ );
97
+ },
98
+ ) ?? (
99
+ <div className="border-border font-mono text-sm border rounded p-4">
100
+ No response body
101
+ </div>
102
+ )}
82
103
  </TabsContent>
83
104
  ))}
84
105
  </ul>
@@ -18,7 +18,7 @@ export const ParameterList = ({
18
18
  <Heading level={3} id={`${id}/${group}-parameters`} className="capitalize">
19
19
  {group === "header" ? "Headers" : `${group} Parameters`}
20
20
  </Heading>
21
- <ul className="list-none m-0 px-0 overflow-hidden">
21
+ <ul className="list-none m-0 px-0 overflow-hidden border border-border divide-y divide-border rounded">
22
22
  {parameters.map((parameter) => (
23
23
  <ParameterListItem
24
24
  key={`${parameter.name}-${parameter.in}`}
@@ -28,14 +28,13 @@ export const ParameterListItem = ({
28
28
  group: ParameterGroup;
29
29
  id: string;
30
30
  }) => (
31
- <li className="not-prose px-2 py-4 border-t border-border bg-border/20 text-sm flex flex-col gap-1">
31
+ <li className="p-4 bg-border/20 text-sm flex flex-col gap-1">
32
32
  <div className="flex items-center gap-2">
33
33
  <code>
34
34
  {group === "path" ? (
35
35
  <ColorizedParam
36
36
  name={parameter.name}
37
37
  backgroundOpacity="15%"
38
- className="px-1"
39
38
  slug={id + "-" + parameter.name.toLocaleLowerCase()}
40
39
  />
41
40
  ) : (
@@ -43,12 +42,12 @@ export const ParameterListItem = ({
43
42
  )}
44
43
  </code>
45
44
  {parameter.required && (
46
- <span className="py-px px-1.5 font-medium text-xs bg-primary/75 text-muted rounded-lg">
45
+ <span className="py-px px-1.5 font-medium bg-primary/75 text-muted rounded-lg">
47
46
  required
48
47
  </span>
49
48
  )}
50
49
  {getParameterSchema(parameter).type && (
51
- <span className="text-xs text-muted-foreground">
50
+ <span className="text-muted-foreground">
52
51
  {getParameterSchema(parameter).type}
53
52
  </span>
54
53
  )}
@@ -1,9 +1,7 @@
1
- import { useState } from "react";
2
1
  import { SyntaxHighlight } from "../../components/SyntaxHighlight.js";
3
2
  import { type SchemaObject } from "../../oas/graphql/index.js";
4
3
  import type { OperationListItemResult } from "./OperationList.js";
5
4
  import * as SidecarBox from "./SidecarBox.js";
6
- import { SimpleSelect } from "./SimpleSelect.js";
7
5
  import { generateSchemaExample } from "./util/generateSchemaExample.js";
8
6
 
9
7
  type Content = NonNullable<
@@ -12,24 +10,13 @@ type Content = NonNullable<
12
10
 
13
11
  // @todo should we handle multiple content types?
14
12
  export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
15
- const [selected, setSelected] = useState("example");
16
-
17
13
  if (!content.length) return null;
18
14
 
19
15
  return (
20
16
  <>
21
- <div>lol</div>
22
17
  <SidecarBox.Root>
23
18
  <SidecarBox.Head className="text-xs flex justify-between items-center">
24
- <span className="font-mono">Request Body</span>
25
- <SimpleSelect
26
- value={selected}
27
- onChange={(e) => setSelected(e.target.value)}
28
- options={[
29
- { value: "example", label: "Example" },
30
- { value: "schema", label: "Schema" },
31
- ]}
32
- />
19
+ <span className="font-mono">Request Body Example</span>
33
20
  </SidecarBox.Head>
34
21
  <SidecarBox.Body>
35
22
  <SyntaxHighlight
@@ -38,9 +25,7 @@ export const RequestBodySidecarBox = ({ content }: { content: Content }) => {
38
25
  copyable
39
26
  className="text-xs"
40
27
  code={JSON.stringify(
41
- selected === "example"
42
- ? generateSchemaExample(content[0].schema as SchemaObject)
43
- : content[0].schema,
28
+ generateSchemaExample(content[0].schema as SchemaObject),
44
29
  null,
45
30
  2,
46
31
  )}