@gitbook/react-openapi 1.0.3 → 1.0.5

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 (68) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/InteractiveSection.jsx +10 -9
  3. package/dist/OpenAPICodeSample.jsx +8 -9
  4. package/dist/OpenAPIDisclosure.d.ts +2 -1
  5. package/dist/OpenAPIDisclosure.jsx +2 -1
  6. package/dist/OpenAPIDisclosureGroup.d.ts +1 -1
  7. package/dist/OpenAPIDisclosureGroup.jsx +3 -2
  8. package/dist/OpenAPIOperation.jsx +2 -2
  9. package/dist/OpenAPIPath.d.ts +3 -2
  10. package/dist/OpenAPIPath.jsx +4 -15
  11. package/dist/OpenAPIRequestBody.jsx +1 -1
  12. package/dist/OpenAPIResponse.jsx +1 -1
  13. package/dist/OpenAPIResponseExample.jsx +10 -10
  14. package/dist/OpenAPIResponses.d.ts +1 -1
  15. package/dist/OpenAPIResponses.jsx +5 -5
  16. package/dist/OpenAPISchema.d.ts +5 -1
  17. package/dist/OpenAPISchema.jsx +30 -21
  18. package/dist/OpenAPISchemaName.d.ts +4 -3
  19. package/dist/OpenAPISchemaName.jsx +1 -1
  20. package/dist/OpenAPISecurities.jsx +2 -2
  21. package/dist/OpenAPISpec.jsx +3 -4
  22. package/dist/OpenAPITabs.d.ts +3 -3
  23. package/dist/OpenAPITabs.jsx +52 -49
  24. package/dist/ScalarApiButton.jsx +1 -1
  25. package/dist/StaticSection.d.ts +10 -0
  26. package/dist/StaticSection.jsx +23 -0
  27. package/dist/code-samples.js +11 -11
  28. package/dist/generateSchemaExample.js +2 -1
  29. package/dist/resolveOpenAPIOperation.d.ts +3 -3
  30. package/dist/resolveOpenAPIOperation.js +1 -1
  31. package/dist/tsconfig.build.tsbuildinfo +1 -1
  32. package/dist/useSyncedTabsGlobalState.d.ts +10 -1
  33. package/dist/useSyncedTabsGlobalState.js +19 -15
  34. package/dist/util/server.d.ts +1 -1
  35. package/dist/util/server.js +1 -3
  36. package/dist/utils.d.ts +1 -1
  37. package/dist/utils.js +4 -6
  38. package/package.json +2 -7
  39. package/src/InteractiveSection.tsx +13 -21
  40. package/src/OpenAPICodeSample.tsx +11 -12
  41. package/src/OpenAPIDisclosure.tsx +5 -3
  42. package/src/OpenAPIDisclosureGroup.tsx +13 -11
  43. package/src/OpenAPIOperation.tsx +3 -3
  44. package/src/OpenAPIOperationContext.tsx +1 -1
  45. package/src/OpenAPIPath.tsx +11 -10
  46. package/src/OpenAPIRequestBody.tsx +2 -2
  47. package/src/OpenAPIResponse.tsx +3 -3
  48. package/src/OpenAPIResponseExample.tsx +12 -19
  49. package/src/OpenAPIResponses.tsx +7 -7
  50. package/src/OpenAPISchema.test.ts +5 -5
  51. package/src/OpenAPISchema.tsx +77 -27
  52. package/src/OpenAPISchemaName.tsx +5 -4
  53. package/src/OpenAPISecurities.tsx +3 -3
  54. package/src/OpenAPISpec.tsx +3 -5
  55. package/src/OpenAPITabs.tsx +56 -67
  56. package/src/ScalarApiButton.tsx +3 -3
  57. package/src/StaticSection.tsx +59 -0
  58. package/src/code-samples.test.ts +66 -66
  59. package/src/code-samples.ts +14 -14
  60. package/src/generateSchemaExample.ts +3 -3
  61. package/src/json2xml.test.ts +1 -1
  62. package/src/resolveOpenAPIOperation.test.ts +6 -6
  63. package/src/resolveOpenAPIOperation.ts +7 -7
  64. package/src/stringifyOpenAPI.ts +1 -1
  65. package/src/useSyncedTabsGlobalState.ts +33 -21
  66. package/src/util/server.test.ts +3 -3
  67. package/src/util/server.ts +2 -3
  68. package/src/utils.ts +4 -4
@@ -4,11 +4,11 @@ import { useId } from 'react';
4
4
 
5
5
  import { InteractiveSection } from './InteractiveSection';
6
6
  import { Markdown } from './Markdown';
7
+ import { OpenAPIDisclosure } from './OpenAPIDisclosure';
8
+ import { OpenAPISchemaName } from './OpenAPISchemaName';
9
+ import { stringifyOpenAPI } from './stringifyOpenAPI';
7
10
  import type { OpenAPIClientContext } from './types';
8
11
  import { checkIsReference, resolveDescription } from './utils';
9
- import { stringifyOpenAPI } from './stringifyOpenAPI';
10
- import { OpenAPISchemaName } from './OpenAPISchemaName';
11
- import { OpenAPIDisclosure } from './OpenAPIDisclosure';
12
12
 
13
13
  type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
14
14
 
@@ -27,7 +27,7 @@ export function OpenAPISchemaProperty(
27
27
  circularRefs?: CircularRefsIds;
28
28
  context: OpenAPIClientContext;
29
29
  className?: string;
30
- },
30
+ }
31
31
  ) {
32
32
  const {
33
33
  schema,
@@ -49,20 +49,13 @@ export function OpenAPISchemaProperty(
49
49
 
50
50
  if (alternatives?.[0]?.length) {
51
51
  return (
52
- <InteractiveSection id={id} className={clsx('openapi-schema', className)}>
53
- <OpenAPISchemaPresentation {...props} />
54
- {alternatives[0].map((alternative, index) => (
55
- <OpenAPISchemaAlternative
56
- key={`alternative-${index}`}
57
- schema={alternative}
58
- circularRefs={circularRefs}
59
- context={context}
60
- />
61
- ))}
62
- {parentCircularRef ? (
63
- <OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
64
- ) : null}
65
- </InteractiveSection>
52
+ <OpenAPISchemaAlternativesItem
53
+ {...props}
54
+ circularRefs={circularRefs}
55
+ context={context}
56
+ alternatives={alternatives}
57
+ parentCircularRef={parentCircularRef}
58
+ />
66
59
  );
67
60
  }
68
61
 
@@ -126,9 +119,9 @@ export function OpenAPISchemaProperties(props: {
126
119
 
127
120
  return (
128
121
  <div id={id} className="openapi-schema-properties">
129
- {properties.map((property) => (
122
+ {properties.map((property, index) => (
130
123
  <OpenAPISchemaProperty
131
- key={property.propertyName}
124
+ key={index}
132
125
  circularRefs={circularRefs}
133
126
  {...property}
134
127
  context={context}
@@ -173,6 +166,25 @@ function OpenAPISchemaAlternative(props: {
173
166
  const id = useId();
174
167
  const subProperties = getSchemaProperties(schema);
175
168
  const description = resolveDescription(schema);
169
+ const alternatives = getSchemaAlternatives(schema, new Set(circularRefs?.keys()));
170
+
171
+ if (alternatives?.[0]?.length && !subProperties?.length) {
172
+ return (
173
+ <>
174
+ {description ? (
175
+ <Markdown source={description} className="openapi-schema-description" />
176
+ ) : null}
177
+ <OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
178
+ <OpenAPISchemaAlternativesItem
179
+ schema={schema}
180
+ circularRefs={circularRefs}
181
+ context={context}
182
+ alternatives={alternatives}
183
+ />
184
+ </OpenAPIDisclosure>
185
+ </>
186
+ );
187
+ }
176
188
 
177
189
  return (
178
190
  <>
@@ -193,6 +205,35 @@ function OpenAPISchemaAlternative(props: {
193
205
  );
194
206
  }
195
207
 
208
+ function OpenAPISchemaAlternativesItem(
209
+ props: OpenAPISchemaPropertyEntry & {
210
+ circularRefs?: CircularRefsIds;
211
+ context: OpenAPIClientContext;
212
+ alternatives: OpenAPISchemaAlternatives;
213
+ parentCircularRef?: string;
214
+ }
215
+ ) {
216
+ const id = useId();
217
+ const { schema, circularRefs, context, alternatives, parentCircularRef } = props;
218
+
219
+ return (
220
+ <InteractiveSection id={id} className={clsx('openapi-schema')}>
221
+ <OpenAPISchemaPresentation {...props} />
222
+ {alternatives[0].map((alternative, index) => (
223
+ <OpenAPISchemaAlternative
224
+ key={`alternative-${index}`}
225
+ schema={alternative}
226
+ circularRefs={circularRefs}
227
+ context={context}
228
+ />
229
+ ))}
230
+ {parentCircularRef ? (
231
+ <OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
232
+ ) : null}
233
+ </InteractiveSection>
234
+ );
235
+ }
236
+
196
237
  /**
197
238
  * Render a circular reference to a schema.
198
239
  */
@@ -336,13 +377,18 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
336
377
  return null;
337
378
  }
338
379
 
380
+ type OpenAPISchemaAlternatives = [
381
+ OpenAPIV3.SchemaObject[],
382
+ OpenAPIV3.DiscriminatorObject | undefined,
383
+ ];
384
+
339
385
  /**
340
386
  * Get the alternatives to display for a schema.
341
387
  */
342
388
  export function getSchemaAlternatives(
343
389
  schema: OpenAPIV3.SchemaObject,
344
- ancestors: Set<OpenAPIV3.SchemaObject> = new Set(),
345
- ): null | [OpenAPIV3.SchemaObject[], OpenAPIV3.DiscriminatorObject | undefined] {
390
+ ancestors: Set<OpenAPIV3.SchemaObject> = new Set()
391
+ ): null | OpenAPISchemaAlternatives {
346
392
  const downAncestors = new Set(ancestors).add(schema);
347
393
 
348
394
  if (schema.anyOf) {
@@ -363,14 +409,16 @@ export function getSchemaAlternatives(
363
409
  function flattenAlternatives(
364
410
  alternativeType: 'oneOf' | 'allOf' | 'anyOf',
365
411
  alternatives: OpenAPIV3.SchemaObject[],
366
- ancestors: Set<OpenAPIV3.SchemaObject>,
412
+ ancestors: Set<OpenAPIV3.SchemaObject>
367
413
  ): OpenAPIV3.SchemaObject[] {
368
414
  return alternatives.reduce((acc, alternative) => {
369
415
  if (!!alternative[alternativeType] && !ancestors.has(alternative)) {
370
- return [...acc, ...(getSchemaAlternatives(alternative, ancestors)?.[0] || [])];
416
+ acc.push(...(getSchemaAlternatives(alternative, ancestors)?.[0] || []));
417
+ } else {
418
+ acc.push(alternative);
371
419
  }
372
420
 
373
- return [...acc, alternative];
421
+ return acc;
374
422
  }, [] as OpenAPIV3.SchemaObject[]);
375
423
  }
376
424
 
@@ -378,7 +426,7 @@ export function getSchemaTitle(
378
426
  schema: OpenAPIV3.SchemaObject,
379
427
 
380
428
  /** If the title is inferred in a oneOf with discriminator, we can use it to optimize the title */
381
- discriminator?: OpenAPIV3.DiscriminatorObject,
429
+ discriminator?: OpenAPIV3.DiscriminatorObject
382
430
  ): string {
383
431
  // Try using the discriminator
384
432
  if (discriminator?.propertyName && schema.properties) {
@@ -406,7 +454,9 @@ export function getSchemaTitle(
406
454
  if (schema.format) {
407
455
  type += ` · ${schema.format}`;
408
456
  }
409
- } else if ('anyOf' in schema) {
457
+ }
458
+
459
+ if ('anyOf' in schema) {
410
460
  type = 'any of';
411
461
  } else if ('oneOf' in schema) {
412
462
  type = 'one of';
@@ -1,8 +1,9 @@
1
- import { OpenAPIV3 } from '@gitbook/openapi-parser';
1
+ import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
+ import type React from 'react';
2
3
 
3
4
  interface OpenAPISchemaNameProps {
4
5
  schema?: OpenAPIV3.SchemaObject;
5
- propertyName?: string | JSX.Element;
6
+ propertyName?: string | React.JSX.Element;
6
7
  required?: boolean;
7
8
  type?: string;
8
9
  }
@@ -11,7 +12,7 @@ interface OpenAPISchemaNameProps {
11
12
  * Display the schema name row.
12
13
  * It includes the property name, type, required and deprecated status.
13
14
  */
14
- export function OpenAPISchemaName(props: OpenAPISchemaNameProps): JSX.Element {
15
+ export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
15
16
  const { schema, type, propertyName, required } = props;
16
17
 
17
18
  const additionalItems = schema && getAdditionalItems(schema);
@@ -52,7 +53,7 @@ function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
52
53
  }
53
54
 
54
55
  if (schema.nullable) {
55
- additionalItems = ` | nullable`;
56
+ additionalItems = ' | nullable';
56
57
  }
57
58
 
58
59
  return additionalItems;
@@ -1,8 +1,8 @@
1
1
  import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
2
- import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
3
2
  import { InteractiveSection } from './InteractiveSection';
4
3
  import { Markdown } from './Markdown';
5
4
  import { OpenAPISchemaName } from './OpenAPISchemaName';
5
+ import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
6
6
  import { resolveDescription } from './utils';
7
7
 
8
8
  /**
@@ -65,7 +65,7 @@ function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) {
65
65
  return <OpenAPISchemaName propertyName="Authorization" type="string" required />;
66
66
  }
67
67
 
68
- if (security.scheme == 'bearer') {
68
+ if (security.scheme === 'bearer') {
69
69
  const description = resolveDescription(security);
70
70
  return (
71
71
  <>
@@ -73,7 +73,7 @@ function getLabelForType(security: OpenAPIV3_1.SecuritySchemeObject) {
73
73
  {/** Show a default description if none is provided */}
74
74
  {!description ? (
75
75
  <Markdown
76
- source={`Bearer authentication header of the form Bearer ${`&lt;token&gt;`}.`}
76
+ source={`Bearer authentication header of the form Bearer ${'&lt;token&gt;'}.`}
77
77
  className="openapi-securities-description"
78
78
  />
79
79
  ) : null}
@@ -1,12 +1,10 @@
1
- 'use client';
2
-
3
1
  import type { OpenAPI } from '@gitbook/openapi-parser';
4
2
 
5
- import { InteractiveSection } from './InteractiveSection';
6
3
  import { OpenAPIRequestBody } from './OpenAPIRequestBody';
7
4
  import { OpenAPIResponses } from './OpenAPIResponses';
8
5
  import { OpenAPISchemaProperties } from './OpenAPISchema';
9
6
  import { OpenAPISecurities } from './OpenAPISecurities';
7
+ import { StaticSection } from './StaticSection';
10
8
  import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
11
9
  import { parameterToProperty } from './utils';
12
10
 
@@ -32,7 +30,7 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
32
30
 
33
31
  {parameterGroups.map((group) => {
34
32
  return (
35
- <InteractiveSection
33
+ <StaticSection
36
34
  key={group.key}
37
35
  className="openapi-parameters"
38
36
  header={group.label}
@@ -41,7 +39,7 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
41
39
  properties={group.parameters.map(parameterToProperty)}
42
40
  context={context}
43
41
  />
44
- </InteractiveSection>
42
+ </StaticSection>
45
43
  );
46
44
  })}
47
45
 
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { createContext, useContext, useEffect, useMemo, useState } from 'react';
4
- import { Key, Tab, TabList, TabPanel, Tabs, TabsProps } from 'react-aria-components';
3
+ import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { type Key, Tab, TabList, TabPanel, Tabs, type TabsProps } from 'react-aria-components';
5
+ import { useEventCallback } from 'usehooks-ts';
5
6
  import { Markdown } from './Markdown';
6
- import { useSyncedTabsGlobalState } from './useSyncedTabsGlobalState';
7
- import { useIntersectionObserver } from 'usehooks-ts';
7
+ import { getOrCreateTabStoreByKey } from './useSyncedTabsGlobalState';
8
8
 
9
- export type Tab = {
9
+ export type TabItem = {
10
10
  key: Key;
11
11
  label: string;
12
12
  body: React.ReactNode;
@@ -14,8 +14,8 @@ export type Tab = {
14
14
  };
15
15
 
16
16
  type OpenAPITabsContextData = {
17
- items: Tab[];
18
- selectedTab: Tab;
17
+ items: TabItem[];
18
+ selectedTab: TabItem | null;
19
19
  };
20
20
 
21
21
  const OpenAPITabsContext = createContext<OpenAPITabsContextData | null>(null);
@@ -32,71 +32,57 @@ function useOpenAPITabsContext() {
32
32
  * The OpenAPI Tabs wrapper component.
33
33
  */
34
34
  export function OpenAPITabs(
35
- props: React.PropsWithChildren<TabsProps & { items: Tab[]; stateKey?: string }>,
35
+ props: React.PropsWithChildren<TabsProps & { items: TabItem[]; stateKey?: string }>
36
36
  ) {
37
37
  const { children, items, stateKey } = props;
38
- const isVisible = stateKey
39
- ? useIntersectionObserver({
40
- threshold: 0.1,
41
- rootMargin: '200px',
42
- })
43
- : true;
44
- const defaultTab = items[0] as Tab;
45
- const [syncedTabs, setSyncedTabs] = useSyncedTabsGlobalState<Tab>();
46
- const [selectedTabKey, setSelectedTabKey] = useState(() => {
47
- if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
48
- const tabFromState = syncedTabs.get(stateKey);
49
- return tabFromState?.key ?? items[0]?.key;
38
+ const [tabKey, setTabKey] = useState<Key | null>(() => {
39
+ if (stateKey && typeof window !== 'undefined') {
40
+ const store = getOrCreateTabStoreByKey(stateKey);
41
+ const tabKey = store.getState().tabKey;
42
+ if (tabKey) {
43
+ return tabKey;
44
+ }
50
45
  }
51
- return items[0]?.key;
46
+ return items[0]?.key ?? null;
52
47
  });
53
- const [selectedTab, setSelectedTab] = useState<Tab>(defaultTab);
54
-
55
- const handleSelectionChange = (key: Key) => {
56
- setSelectedTabKey(key);
57
- if (stateKey) {
58
- const tab = items.find((item) => item.key === key);
59
-
60
- if (!tab) {
61
- return;
62
- }
63
-
64
- setSyncedTabs((state) => {
65
- const newState = new Map(state);
66
- newState.set(stateKey, tab);
67
- return newState;
68
- });
48
+ const selectTab = useEventCallback((key: Key | null) => {
49
+ if (!key || key === tabKey) {
50
+ return;
69
51
  }
70
- };
71
-
52
+ const tab = items.find((item) => item.key === key);
53
+ if (!tab) {
54
+ return;
55
+ }
56
+ setTabKey(key);
57
+ });
58
+ const selectedTab = items.find((item) => item.key === tabKey) ?? items[0] ?? null;
59
+ const cancelDeferRef = useRef<(() => void) | null>(null);
72
60
  useEffect(() => {
73
- if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
74
- const tabFromState = syncedTabs.get(stateKey);
75
-
76
- if (!items.some((item) => item.key === tabFromState?.key)) {
77
- return setSelectedTab(defaultTab);
78
- }
79
-
80
- if (tabFromState && tabFromState?.key !== selectedTab?.key) {
81
- const tabFromItems = items.find((item) => item.key === tabFromState.key);
82
-
83
- if (!tabFromItems) {
84
- return;
85
- }
86
-
87
- setSelectedTab(tabFromItems);
88
- }
61
+ if (!stateKey) {
62
+ return undefined;
89
63
  }
90
- }, [isVisible, stateKey, syncedTabs, selectedTabKey]);
91
-
64
+ const store = getOrCreateTabStoreByKey(stateKey);
65
+ return store.subscribe((state) => {
66
+ cancelDeferRef.current?.();
67
+ cancelDeferRef.current = defer(() => selectTab(state.tabKey));
68
+ });
69
+ }, [stateKey, selectTab]);
70
+ useEffect(() => {
71
+ return () => cancelDeferRef.current?.();
72
+ }, []);
92
73
  const contextValue = useMemo(() => ({ items, selectedTab }), [items, selectedTab]);
93
-
94
74
  return (
95
75
  <OpenAPITabsContext.Provider value={contextValue}>
96
76
  <Tabs
97
77
  className="openapi-tabs"
98
- onSelectionChange={handleSelectionChange}
99
- selectedKey={selectedTab?.key}
78
+ onSelectionChange={(tabKey) => {
79
+ selectTab(tabKey);
80
+ if (stateKey) {
81
+ const store = getOrCreateTabStoreByKey(stateKey);
82
+ store.setState({ tabKey });
83
+ }
84
+ }}
85
+ selectedKey={tabKey}
100
86
  >
101
87
  {children}
102
88
  </Tabs>
@@ -104,6 +90,11 @@ export function OpenAPITabs(
104
90
  );
105
91
  }
106
92
 
93
+ const defer = (fn: () => void) => {
94
+ const id = setTimeout(fn, 0);
95
+ return () => clearTimeout(id);
96
+ };
97
+
107
98
  /**
108
99
  * The OpenAPI Tabs list component.
109
100
  * This component should be used as a child of the OpenAPITabs component.
@@ -116,14 +107,14 @@ export function OpenAPITabsList() {
116
107
  <TabList className="openapi-tabs-list">
117
108
  {items.map((tab) => (
118
109
  <Tab
110
+ key={tab.key}
111
+ id={tab.key}
119
112
  style={({ isFocusVisible }) => ({
120
113
  outline: isFocusVisible
121
114
  ? '2px solid rgb(var(--primary-color-500)/0.4)'
122
115
  : 'none',
123
116
  })}
124
117
  className="openapi-tabs-tab"
125
- key={`Tab-${tab.key}`}
126
- id={tab.key}
127
118
  >
128
119
  {tab.label}
129
120
  </Tab>
@@ -144,12 +135,10 @@ export function OpenAPITabsPanels() {
144
135
  return null;
145
136
  }
146
137
 
138
+ const key = selectedTab.key.toString();
139
+
147
140
  return (
148
- <TabPanel
149
- key={`TabPanel-${selectedTab.key}`}
150
- id={selectedTab.key.toString()}
151
- className="openapi-tabs-panel"
152
- >
141
+ <TabPanel key={key} id={key} className="openapi-tabs-panel">
153
142
  {selectedTab.body}
154
143
  {selectedTab.description ? (
155
144
  <Markdown source={selectedTab.description} className="openapi-tabs-footer" />
@@ -4,8 +4,8 @@ import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-re
4
4
  import { useEffect, useImperativeHandle, useRef, useState } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
 
7
- import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
8
7
  import { useEventCallback } from 'usehooks-ts';
8
+ import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
9
9
 
10
10
  /**
11
11
  * Button which launches the Scalar API Client
@@ -48,7 +48,7 @@ export function ScalarApiButton({
48
48
  path={path}
49
49
  specUrl={specUrl}
50
50
  />,
51
- document.body,
51
+ document.body
52
52
  )}
53
53
  </div>
54
54
  );
@@ -88,7 +88,7 @@ function ScalarModalController(props: {
88
88
  useImperativeHandle(
89
89
  props.controllerRef,
90
90
  () => ({ openClient: openClient ? () => openClient() : undefined }),
91
- [openClient],
91
+ [openClient]
92
92
  );
93
93
 
94
94
  // Open the client when the component is mounted.
@@ -0,0 +1,59 @@
1
+ import clsx from 'clsx';
2
+ import { type ComponentPropsWithoutRef, forwardRef } from 'react';
3
+
4
+ export function Section(props: ComponentPropsWithoutRef<'div'>) {
5
+ return <div {...props} className={clsx('openapi-section', props.className)} />;
6
+ }
7
+
8
+ export function SectionHeader(props: ComponentPropsWithoutRef<'div'>) {
9
+ return (
10
+ <div
11
+ {...props}
12
+ className={clsx(
13
+ 'openapi-section-header',
14
+ props.className && `${props.className}-header`
15
+ )}
16
+ />
17
+ );
18
+ }
19
+
20
+ export function SectionHeaderContent(props: ComponentPropsWithoutRef<'div'>) {
21
+ return (
22
+ <div
23
+ {...props}
24
+ className={clsx(
25
+ 'openapi-section-header-content',
26
+ props.className && `${props.className}-header-content`
27
+ )}
28
+ />
29
+ );
30
+ }
31
+
32
+ export const SectionBody = forwardRef(function SectionBody(
33
+ props: ComponentPropsWithoutRef<'div'>,
34
+ ref: React.ForwardedRef<HTMLDivElement>
35
+ ) {
36
+ return (
37
+ <div
38
+ ref={ref}
39
+ {...props}
40
+ className={clsx('openapi-section-body', props.className && `${props.className}-body`)}
41
+ />
42
+ );
43
+ });
44
+
45
+ export function StaticSection(props: {
46
+ className: string;
47
+ header: React.ReactNode;
48
+ children: React.ReactNode;
49
+ }) {
50
+ const { className, header, children } = props;
51
+ return (
52
+ <Section className={className}>
53
+ <SectionHeader className={className}>
54
+ <SectionHeaderContent className={className}>{header}</SectionHeaderContent>
55
+ </SectionHeader>
56
+ <SectionBody className={className}>{children}</SectionBody>
57
+ </Section>
58
+ );
59
+ }