@gitbook/react-openapi 1.1.9 → 1.2.0
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.
- package/CHANGELOG.md +27 -0
- package/dist/InteractiveSection.d.ts +4 -0
- package/dist/InteractiveSection.jsx +14 -13
- package/dist/OpenAPICodeSample.d.ts +3 -2
- package/dist/OpenAPICodeSample.jsx +8 -12
- package/dist/OpenAPICodeSampleInteractive.d.ts +3 -0
- package/dist/OpenAPICodeSampleInteractive.jsx +37 -49
- package/dist/OpenAPICodeSampleSelector.d.ts +14 -0
- package/dist/OpenAPICodeSampleSelector.jsx +44 -0
- package/dist/OpenAPICopyButton.d.ts +2 -0
- package/dist/OpenAPICopyButton.jsx +5 -2
- package/dist/OpenAPIDisclosure.d.ts +4 -3
- package/dist/OpenAPIDisclosure.jsx +8 -11
- package/dist/OpenAPIDisclosureGroup.d.ts +7 -3
- package/dist/OpenAPIDisclosureGroup.jsx +18 -18
- package/dist/OpenAPIExample.d.ts +16 -0
- package/dist/OpenAPIExample.jsx +36 -0
- package/dist/OpenAPIMediaType.d.ts +21 -0
- package/dist/OpenAPIMediaType.jsx +61 -0
- package/dist/OpenAPIOperation.d.ts +3 -2
- package/dist/OpenAPIOperation.jsx +9 -72
- package/dist/OpenAPIOperationDescription.d.ts +9 -0
- package/dist/OpenAPIOperationDescription.jsx +22 -0
- package/dist/OpenAPIOperationStability.d.ts +9 -0
- package/dist/OpenAPIOperationStability.jsx +27 -0
- package/dist/OpenAPIPath.d.ts +12 -2
- package/dist/OpenAPIPath.jsx +10 -4
- package/dist/OpenAPIRequestBody.d.ts +3 -1
- package/dist/OpenAPIRequestBody.jsx +4 -3
- package/dist/OpenAPIResponse.d.ts +1 -1
- package/dist/OpenAPIResponse.jsx +1 -1
- package/dist/OpenAPIResponseExample.d.ts +4 -3
- package/dist/OpenAPIResponseExample.jsx +24 -154
- package/dist/OpenAPIResponseExampleContent.d.ts +19 -0
- package/dist/OpenAPIResponseExampleContent.jsx +57 -0
- package/dist/OpenAPIResponses.d.ts +1 -1
- package/dist/OpenAPIResponses.jsx +49 -36
- package/dist/OpenAPISchema.d.ts +1 -1
- package/dist/OpenAPISchema.jsx +121 -20
- package/dist/OpenAPISchemaName.d.ts +2 -0
- package/dist/OpenAPISchemaName.jsx +21 -17
- package/dist/OpenAPISchemaServer.d.ts +1 -1
- package/dist/OpenAPISecurities.d.ts +2 -1
- package/dist/OpenAPISecurities.jsx +11 -10
- package/dist/OpenAPISelect.d.ts +22 -0
- package/dist/OpenAPISelect.jsx +43 -0
- package/dist/OpenAPISpec.d.ts +3 -2
- package/dist/OpenAPISpec.jsx +11 -9
- package/dist/OpenAPITabs.jsx +9 -9
- package/dist/OpenAPIWebhook.d.ts +10 -0
- package/dist/OpenAPIWebhook.jsx +23 -0
- package/dist/OpenAPIWebhookExample.d.ts +6 -0
- package/dist/OpenAPIWebhookExample.jsx +41 -0
- package/dist/ScalarApiButton.d.ts +2 -0
- package/dist/ScalarApiButton.jsx +4 -3
- package/dist/StaticSection.d.ts +4 -1
- package/dist/StaticSection.jsx +13 -4
- package/dist/code-samples.js +57 -39
- package/dist/common/OpenAPIColumnSpec.d.ts +6 -0
- package/dist/common/OpenAPIColumnSpec.jsx +20 -0
- package/dist/common/OpenAPIOperationDescription.d.ts +6 -0
- package/dist/common/OpenAPIOperationDescription.jsx +19 -0
- package/dist/common/OpenAPIStability.d.ts +4 -0
- package/dist/common/OpenAPIStability.jsx +15 -0
- package/dist/common/OpenAPISummary.d.ts +6 -0
- package/dist/common/OpenAPISummary.jsx +30 -0
- package/dist/context.d.ts +75 -0
- package/dist/context.js +43 -0
- package/dist/generateSchemaExample.js +4 -0
- package/dist/getOrCreateStoreByKey.d.ts +10 -0
- package/dist/getOrCreateStoreByKey.js +19 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/resolveOpenAPIOperation.js +10 -5
- package/dist/resolveOpenAPIWebhook.d.ts +11 -0
- package/dist/resolveOpenAPIWebhook.js +127 -0
- package/dist/schemas/OpenAPISchemas.d.ts +5 -6
- package/dist/schemas/OpenAPISchemas.jsx +52 -49
- package/dist/schemas/resolveOpenAPISchemas.d.ts +4 -3
- package/dist/schemas/resolveOpenAPISchemas.js +0 -1
- package/dist/stringifyOpenAPI.d.ts +1 -1
- package/dist/stringifyOpenAPI.js +6 -3
- package/dist/translate.d.ts +10 -0
- package/dist/translate.jsx +75 -0
- package/dist/translations/de.d.ts +37 -0
- package/dist/translations/de.js +37 -0
- package/dist/translations/en.d.ts +37 -0
- package/dist/translations/en.js +37 -0
- package/dist/translations/es.d.ts +37 -0
- package/dist/translations/es.js +37 -0
- package/dist/translations/fr.d.ts +37 -0
- package/dist/translations/fr.js +37 -0
- package/dist/translations/index.d.ts +341 -0
- package/dist/translations/index.js +27 -0
- package/dist/translations/ja.d.ts +37 -0
- package/dist/translations/ja.js +37 -0
- package/dist/translations/nl.d.ts +37 -0
- package/dist/translations/nl.js +37 -0
- package/dist/translations/no.d.ts +37 -0
- package/dist/translations/no.js +37 -0
- package/dist/translations/pt-br.d.ts +37 -0
- package/dist/translations/pt-br.js +37 -0
- package/dist/translations/types.d.ts +5 -0
- package/dist/translations/types.js +1 -0
- package/dist/translations/zh.d.ts +37 -0
- package/dist/translations/zh.js +37 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +12 -48
- package/dist/util/example.d.ts +35 -0
- package/dist/util/example.jsx +103 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.js +57 -0
- package/package.json +3 -3
- package/src/InteractiveSection.tsx +22 -18
- package/src/OpenAPICodeSample.tsx +26 -15
- package/src/OpenAPICodeSampleInteractive.tsx +67 -70
- package/src/OpenAPICodeSampleSelector.tsx +94 -0
- package/src/OpenAPICopyButton.tsx +7 -2
- package/src/OpenAPIDisclosure.tsx +20 -22
- package/src/OpenAPIDisclosureGroup.tsx +40 -22
- package/src/OpenAPIExample.tsx +55 -0
- package/src/OpenAPIMediaType.tsx +139 -0
- package/src/OpenAPIOperation.tsx +11 -104
- package/src/OpenAPIOperationDescription.tsx +34 -0
- package/src/OpenAPIOperationStability.tsx +39 -0
- package/src/OpenAPIPath.tsx +26 -6
- package/src/OpenAPIRequestBody.tsx +9 -4
- package/src/OpenAPIResponse.tsx +2 -2
- package/src/OpenAPIResponseExample.tsx +41 -215
- package/src/OpenAPIResponseExampleContent.tsx +123 -0
- package/src/OpenAPIResponses.tsx +83 -62
- package/src/OpenAPISchema.test.ts +80 -0
- package/src/OpenAPISchema.tsx +149 -25
- package/src/OpenAPISchemaName.tsx +28 -19
- package/src/OpenAPISchemaServer.tsx +1 -1
- package/src/OpenAPISecurities.tsx +46 -12
- package/src/OpenAPISelect.tsx +96 -0
- package/src/OpenAPISpec.tsx +21 -10
- package/src/OpenAPITabs.tsx +9 -9
- package/src/OpenAPIWebhook.tsx +33 -0
- package/src/OpenAPIWebhookExample.tsx +60 -0
- package/src/ScalarApiButton.tsx +6 -6
- package/src/StaticSection.tsx +37 -5
- package/src/code-samples.test.ts +3 -1
- package/src/code-samples.ts +67 -54
- package/src/common/OpenAPIColumnSpec.tsx +31 -0
- package/src/common/OpenAPIOperationDescription.tsx +31 -0
- package/src/common/OpenAPIStability.tsx +23 -0
- package/src/common/OpenAPISummary.tsx +45 -0
- package/src/context.ts +99 -0
- package/src/generateSchemaExample.test.ts +1020 -0
- package/src/generateSchemaExample.ts +5 -0
- package/src/getOrCreateStoreByKey.ts +33 -0
- package/src/index.ts +5 -1
- package/src/resolveOpenAPIOperation.ts +14 -3
- package/src/resolveOpenAPIWebhook.ts +99 -0
- package/src/schemas/OpenAPISchemas.tsx +76 -71
- package/src/schemas/resolveOpenAPISchemas.ts +4 -5
- package/src/stringifyOpenAPI.ts +11 -3
- package/src/translate.tsx +80 -0
- package/src/translations/de.ts +37 -0
- package/src/translations/en.ts +37 -0
- package/src/translations/es.ts +37 -0
- package/src/translations/fr.ts +37 -0
- package/src/translations/index.ts +33 -0
- package/src/translations/ja.ts +37 -0
- package/src/translations/nl.ts +37 -0
- package/src/translations/no.ts +37 -0
- package/src/translations/pt-br.ts +37 -0
- package/src/translations/types.ts +7 -0
- package/src/translations/zh.ts +37 -0
- package/src/types.ts +11 -46
- package/src/util/example.tsx +129 -0
- package/src/utils.ts +67 -0
- package/dist/useSyncedTabsGlobalState.d.ts +0 -10
- package/dist/useSyncedTabsGlobalState.js +0 -20
- package/src/useSyncedTabsGlobalState.ts +0 -35
|
@@ -1,86 +1,70 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
-
import { useCallback } from 'react';
|
|
4
|
-
import { useStore } from 'zustand';
|
|
5
3
|
import type { MediaTypeRenderer } from './OpenAPICodeSample';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
type MediaTypeState = {
|
|
9
|
-
mediaType: string;
|
|
10
|
-
setMediaType: (mediaType: string) => void;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
function useMediaTypeState(
|
|
14
|
-
data: { method: string; path: string },
|
|
15
|
-
defaultKey: string
|
|
16
|
-
): MediaTypeState {
|
|
17
|
-
const { method, path } = data;
|
|
18
|
-
const store = useStore(getOrCreateTabStoreByKey(`media-type-${method}-${path}`, defaultKey));
|
|
19
|
-
if (typeof store.tabKey !== 'string') {
|
|
20
|
-
throw new Error('Media type key is not a string');
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
mediaType: store.tabKey,
|
|
24
|
-
setMediaType: useCallback((index: string) => store.setTabKey(index), [store.setTabKey]),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function useMediaTypeSampleIndexState(data: { method: string; path: string }, mediaType: string) {
|
|
29
|
-
const { method, path } = data;
|
|
30
|
-
const store = useStore(
|
|
31
|
-
getOrCreateTabStoreByKey(`media-type-sample-${mediaType}-${method}-${path}`, 0)
|
|
32
|
-
);
|
|
33
|
-
if (typeof store.tabKey !== 'number') {
|
|
34
|
-
throw new Error('Example key is not a number');
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
index: store.tabKey,
|
|
38
|
-
setIndex: useCallback((index: number) => store.setTabKey(index), [store.setTabKey]),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
4
|
+
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
|
|
5
|
+
import { createStateKey } from './utils';
|
|
41
6
|
|
|
42
7
|
export function OpenAPIMediaTypeExamplesSelector(props: {
|
|
43
8
|
method: string;
|
|
44
9
|
path: string;
|
|
45
10
|
renderers: MediaTypeRenderer[];
|
|
11
|
+
selectIcon?: React.ReactNode;
|
|
12
|
+
blockKey?: string;
|
|
46
13
|
}) {
|
|
47
|
-
const { method, path, renderers } = props;
|
|
14
|
+
const { method, path, renderers, selectIcon, blockKey } = props;
|
|
48
15
|
if (!renderers[0]) {
|
|
49
16
|
throw new Error('No renderers provided');
|
|
50
17
|
}
|
|
51
|
-
const
|
|
52
|
-
const
|
|
18
|
+
const stateKey = createStateKey('request-body-media-type', blockKey);
|
|
19
|
+
const state = useSelectState(stateKey, renderers[0].mediaType);
|
|
20
|
+
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
|
|
53
21
|
|
|
54
22
|
return (
|
|
55
23
|
<div className="openapi-codesample-selectors">
|
|
56
|
-
<MediaTypeSelector
|
|
57
|
-
<ExamplesSelector
|
|
24
|
+
<MediaTypeSelector selectIcon={selectIcon} stateKey={stateKey} renderers={renderers} />
|
|
25
|
+
<ExamplesSelector
|
|
26
|
+
selectIcon={selectIcon}
|
|
27
|
+
method={method}
|
|
28
|
+
path={path}
|
|
29
|
+
renderer={selected}
|
|
30
|
+
/>
|
|
58
31
|
</div>
|
|
59
32
|
);
|
|
60
33
|
}
|
|
61
34
|
|
|
62
35
|
function MediaTypeSelector(props: {
|
|
63
|
-
|
|
36
|
+
stateKey: string;
|
|
64
37
|
renderers: MediaTypeRenderer[];
|
|
38
|
+
selectIcon?: React.ReactNode;
|
|
65
39
|
}) {
|
|
66
|
-
const { renderers,
|
|
40
|
+
const { renderers, stateKey, selectIcon } = props;
|
|
67
41
|
|
|
68
42
|
if (renderers.length < 2) {
|
|
69
43
|
return null;
|
|
70
44
|
}
|
|
71
45
|
|
|
46
|
+
const items = renderers.map((renderer) => ({
|
|
47
|
+
key: renderer.mediaType,
|
|
48
|
+
label: renderer.mediaType,
|
|
49
|
+
}));
|
|
50
|
+
|
|
72
51
|
return (
|
|
73
|
-
<
|
|
52
|
+
<OpenAPISelect
|
|
74
53
|
className={clsx('openapi-select')}
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
items={renderers.map((renderer) => ({
|
|
55
|
+
key: renderer.mediaType,
|
|
56
|
+
label: renderer.mediaType,
|
|
57
|
+
}))}
|
|
58
|
+
icon={selectIcon}
|
|
59
|
+
stateKey={stateKey}
|
|
60
|
+
placement="bottom start"
|
|
77
61
|
>
|
|
78
|
-
{
|
|
79
|
-
<
|
|
80
|
-
{
|
|
81
|
-
</
|
|
62
|
+
{items.map((item) => (
|
|
63
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
64
|
+
{item.label}
|
|
65
|
+
</OpenAPISelectItem>
|
|
82
66
|
))}
|
|
83
|
-
</
|
|
67
|
+
</OpenAPISelect>
|
|
84
68
|
);
|
|
85
69
|
}
|
|
86
70
|
|
|
@@ -88,25 +72,31 @@ function ExamplesSelector(props: {
|
|
|
88
72
|
method: string;
|
|
89
73
|
path: string;
|
|
90
74
|
renderer: MediaTypeRenderer;
|
|
75
|
+
selectIcon?: React.ReactNode;
|
|
91
76
|
}) {
|
|
92
|
-
const { method, path, renderer } = props;
|
|
93
|
-
const state = useMediaTypeSampleIndexState({ method, path }, renderer.mediaType);
|
|
77
|
+
const { method, path, renderer, selectIcon } = props;
|
|
94
78
|
if (renderer.examples.length < 2) {
|
|
95
79
|
return null;
|
|
96
80
|
}
|
|
97
81
|
|
|
82
|
+
const items = renderer.examples.map((example, index) => ({
|
|
83
|
+
key: index,
|
|
84
|
+
label: example.example.summary || `Example ${index + 1}`,
|
|
85
|
+
}));
|
|
86
|
+
|
|
98
87
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
<OpenAPISelect
|
|
89
|
+
items={items}
|
|
90
|
+
icon={selectIcon}
|
|
91
|
+
stateKey={`media-type-sample-${renderer.mediaType}-${method}-${path}`}
|
|
92
|
+
placement="bottom start"
|
|
103
93
|
>
|
|
104
|
-
{
|
|
105
|
-
<
|
|
106
|
-
{
|
|
107
|
-
</
|
|
94
|
+
{items.map((item) => (
|
|
95
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
96
|
+
{item.label}
|
|
97
|
+
</OpenAPISelectItem>
|
|
108
98
|
))}
|
|
109
|
-
</
|
|
99
|
+
</OpenAPISelect>
|
|
110
100
|
);
|
|
111
101
|
}
|
|
112
102
|
|
|
@@ -114,14 +104,18 @@ export function OpenAPIMediaTypeExamplesBody(props: {
|
|
|
114
104
|
method: string;
|
|
115
105
|
path: string;
|
|
116
106
|
renderers: MediaTypeRenderer[];
|
|
107
|
+
blockKey?: string;
|
|
117
108
|
}) {
|
|
118
|
-
const { renderers, method, path } = props;
|
|
109
|
+
const { renderers, method, path, blockKey } = props;
|
|
119
110
|
if (!renderers[0]) {
|
|
120
111
|
throw new Error('No renderers provided');
|
|
121
112
|
}
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
113
|
+
|
|
114
|
+
const mediaTypeState = useSelectState(
|
|
115
|
+
createStateKey('request-body-media-type', blockKey),
|
|
116
|
+
renderers[0].mediaType
|
|
117
|
+
);
|
|
118
|
+
const selected = renderers.find((r) => r.mediaType === mediaTypeState.key) ?? renderers[0];
|
|
125
119
|
if (selected.examples.length === 0) {
|
|
126
120
|
return selected.element;
|
|
127
121
|
}
|
|
@@ -130,10 +124,13 @@ export function OpenAPIMediaTypeExamplesBody(props: {
|
|
|
130
124
|
|
|
131
125
|
function ExamplesBody(props: { method: string; path: string; renderer: MediaTypeRenderer }) {
|
|
132
126
|
const { method, path, renderer } = props;
|
|
133
|
-
const exampleState =
|
|
134
|
-
|
|
127
|
+
const exampleState = useSelectState(
|
|
128
|
+
`media-type-sample-${renderer.mediaType}-${method}-${path}`,
|
|
129
|
+
renderer.mediaType
|
|
130
|
+
);
|
|
131
|
+
const example = renderer.examples[Number(exampleState.key)] ?? renderer.examples[0];
|
|
135
132
|
if (!example) {
|
|
136
|
-
throw new Error(`No example found for
|
|
133
|
+
throw new Error(`No example found for key ${exampleState.key}`);
|
|
137
134
|
}
|
|
138
135
|
return example.element;
|
|
139
136
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import type { Key } from 'react-aria';
|
|
5
|
+
import { useStore } from 'zustand';
|
|
6
|
+
import { OpenAPIPath } from './OpenAPIPath';
|
|
7
|
+
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
|
8
|
+
import { StaticSection } from './StaticSection';
|
|
9
|
+
import type { OpenAPIClientContext } from './context';
|
|
10
|
+
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
|
11
|
+
import type { OpenAPIOperationData } from './types';
|
|
12
|
+
|
|
13
|
+
function useCodeSampleState(initialKey: Key = 'default') {
|
|
14
|
+
const store = useStore(getOrCreateStoreByKey('codesample', initialKey));
|
|
15
|
+
return {
|
|
16
|
+
key: store.key,
|
|
17
|
+
setKey: useCallback((key: Key) => store.setKey(key), [store.setKey]),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type CodeSampleItem = OpenAPISelectItem & {
|
|
22
|
+
body: React.ReactNode;
|
|
23
|
+
footer?: React.ReactNode;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function OpenAPICodeSampleHeader(props: {
|
|
27
|
+
items: CodeSampleItem[];
|
|
28
|
+
data: OpenAPIOperationData;
|
|
29
|
+
selectIcon?: React.ReactNode;
|
|
30
|
+
context: OpenAPIClientContext;
|
|
31
|
+
}) {
|
|
32
|
+
const { data, items, selectIcon, context } = props;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
|
|
37
|
+
{items.length > 1 ? (
|
|
38
|
+
<OpenAPISelect
|
|
39
|
+
icon={selectIcon}
|
|
40
|
+
items={items}
|
|
41
|
+
stateKey="codesample"
|
|
42
|
+
placement="bottom end"
|
|
43
|
+
>
|
|
44
|
+
{items.map((item) => (
|
|
45
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
46
|
+
{item.label}
|
|
47
|
+
</OpenAPISelectItem>
|
|
48
|
+
))}
|
|
49
|
+
</OpenAPISelect>
|
|
50
|
+
) : items[0] ? (
|
|
51
|
+
<span className="openapi-codesample-label">{items[0].label}</span>
|
|
52
|
+
) : null}
|
|
53
|
+
</>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function OpenAPICodeSampleBody(props: {
|
|
58
|
+
items: CodeSampleItem[];
|
|
59
|
+
data: OpenAPIOperationData;
|
|
60
|
+
selectIcon?: React.ReactNode;
|
|
61
|
+
context: OpenAPIClientContext;
|
|
62
|
+
}) {
|
|
63
|
+
const { items, data, selectIcon, context } = props;
|
|
64
|
+
if (!items[0]) {
|
|
65
|
+
throw new Error('No items provided');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const state = useCodeSampleState(items[0]?.key);
|
|
69
|
+
|
|
70
|
+
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
71
|
+
|
|
72
|
+
if (!selected) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<StaticSection
|
|
78
|
+
header={
|
|
79
|
+
<OpenAPICodeSampleHeader
|
|
80
|
+
context={context}
|
|
81
|
+
selectIcon={selectIcon}
|
|
82
|
+
data={data}
|
|
83
|
+
items={items}
|
|
84
|
+
/>
|
|
85
|
+
}
|
|
86
|
+
className="openapi-codesample"
|
|
87
|
+
>
|
|
88
|
+
<div id={selected.key as string} className="openapi-codesample-panel">
|
|
89
|
+
{selected.body ? selected.body : null}
|
|
90
|
+
{selected.footer ? selected.footer : null}
|
|
91
|
+
</div>
|
|
92
|
+
</StaticSection>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-components';
|
|
5
|
+
import type { OpenAPIClientContext } from './context';
|
|
6
|
+
import { t } from './translate';
|
|
5
7
|
|
|
6
8
|
export function OpenAPICopyButton(
|
|
7
9
|
props: ButtonProps & {
|
|
8
10
|
value: string;
|
|
9
11
|
children: React.ReactNode;
|
|
12
|
+
context: OpenAPIClientContext;
|
|
10
13
|
label?: string;
|
|
11
14
|
/**
|
|
12
15
|
* Whether to show a tooltip.
|
|
@@ -15,7 +18,7 @@ export function OpenAPICopyButton(
|
|
|
15
18
|
withTooltip?: boolean;
|
|
16
19
|
}
|
|
17
20
|
) {
|
|
18
|
-
const { value, label, children, onPress, className, withTooltip = true } = props;
|
|
21
|
+
const { value, label, children, onPress, className, context, withTooltip = true } = props;
|
|
19
22
|
const [copied, setCopied] = useState(false);
|
|
20
23
|
const [isOpen, setIsOpen] = useState(false);
|
|
21
24
|
|
|
@@ -60,7 +63,9 @@ export function OpenAPICopyButton(
|
|
|
60
63
|
offset={4}
|
|
61
64
|
className="openapi-tooltip"
|
|
62
65
|
>
|
|
63
|
-
{copied
|
|
66
|
+
{copied
|
|
67
|
+
? t(context.translation, 'copied')
|
|
68
|
+
: label || t(context.translation, 'copy_to_clipboard')}
|
|
64
69
|
</Tooltip>
|
|
65
70
|
</TooltipTrigger>
|
|
66
71
|
);
|
|
@@ -1,41 +1,39 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import type React from 'react';
|
|
2
4
|
import { useState } from 'react';
|
|
3
|
-
import { Button, Disclosure, DisclosurePanel
|
|
4
|
-
import type { OpenAPIClientContext } from './types';
|
|
5
|
+
import { Button, Disclosure, DisclosurePanel } from 'react-aria-components';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Display an interactive OpenAPI disclosure.
|
|
8
9
|
*/
|
|
9
10
|
export function OpenAPIDisclosure(props: {
|
|
10
|
-
|
|
11
|
+
icon: React.ReactNode;
|
|
11
12
|
children: React.ReactNode;
|
|
12
|
-
label: string;
|
|
13
|
+
label: string | ((isExpanded: boolean) => string);
|
|
14
|
+
className?: string;
|
|
13
15
|
}): React.JSX.Element {
|
|
14
|
-
const {
|
|
16
|
+
const { icon, children, label, className } = props;
|
|
15
17
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
16
18
|
|
|
17
19
|
return (
|
|
18
20
|
<Disclosure
|
|
19
|
-
className=
|
|
21
|
+
className={clsx('openapi-disclosure', className)}
|
|
20
22
|
isExpanded={isExpanded}
|
|
21
23
|
onExpandedChange={setIsExpanded}
|
|
22
24
|
>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
{isExpanded ? 'Hide' : 'Show'} {label}
|
|
36
|
-
</span>
|
|
37
|
-
</Button>
|
|
38
|
-
</Heading>
|
|
25
|
+
<Button
|
|
26
|
+
slot="trigger"
|
|
27
|
+
className="openapi-disclosure-trigger"
|
|
28
|
+
style={({ isFocusVisible }) => ({
|
|
29
|
+
outline: isFocusVisible
|
|
30
|
+
? '2px solid rgb(var(--primary-color-500) / 0.4)'
|
|
31
|
+
: 'none',
|
|
32
|
+
})}
|
|
33
|
+
>
|
|
34
|
+
{icon}
|
|
35
|
+
<span>{typeof label === 'function' ? label(isExpanded) : label}</span>
|
|
36
|
+
</Button>
|
|
39
37
|
<DisclosurePanel className="openapi-disclosure-panel">
|
|
40
38
|
{isExpanded ? children : null}
|
|
41
39
|
</DisclosurePanel>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { createContext, useContext, useRef
|
|
3
|
+
import { createContext, useContext, useRef } from 'react';
|
|
4
4
|
import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
|
|
5
5
|
import {
|
|
6
6
|
type DisclosureGroupProps,
|
|
@@ -8,18 +8,23 @@ import {
|
|
|
8
8
|
useDisclosureGroupState,
|
|
9
9
|
useDisclosureState,
|
|
10
10
|
} from 'react-stately';
|
|
11
|
+
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
|
|
11
12
|
|
|
12
13
|
interface Props {
|
|
13
14
|
groups: TDisclosureGroup[];
|
|
14
15
|
icon?: React.ReactNode;
|
|
16
|
+
/** State key to use with a store */
|
|
17
|
+
selectStateKey?: string;
|
|
18
|
+
/** Icon to display for the select */
|
|
19
|
+
selectIcon?: React.ReactNode;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
22
|
type TDisclosureGroup = {
|
|
18
|
-
|
|
23
|
+
key: string;
|
|
19
24
|
label: string | React.ReactNode;
|
|
20
25
|
tabs?: {
|
|
21
|
-
|
|
22
|
-
label
|
|
26
|
+
key: string;
|
|
27
|
+
label: string | React.ReactNode;
|
|
23
28
|
body?: React.ReactNode;
|
|
24
29
|
}[];
|
|
25
30
|
};
|
|
@@ -30,24 +35,35 @@ const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(n
|
|
|
30
35
|
* Display an interactive OpenAPI disclosure group.
|
|
31
36
|
*/
|
|
32
37
|
export function OpenAPIDisclosureGroup(props: DisclosureGroupProps & Props) {
|
|
33
|
-
const { icon, groups } = props;
|
|
38
|
+
const { icon, groups, selectStateKey, selectIcon } = props;
|
|
34
39
|
|
|
35
40
|
const state = useDisclosureGroupState(props);
|
|
36
41
|
|
|
37
42
|
return (
|
|
38
43
|
<DisclosureGroupStateContext.Provider value={state}>
|
|
39
44
|
{groups.map((group) => (
|
|
40
|
-
<DisclosureItem
|
|
45
|
+
<DisclosureItem
|
|
46
|
+
selectStateKey={selectStateKey}
|
|
47
|
+
selectIcon={selectIcon}
|
|
48
|
+
icon={icon}
|
|
49
|
+
key={group.key}
|
|
50
|
+
group={group}
|
|
51
|
+
/>
|
|
41
52
|
))}
|
|
42
53
|
</DisclosureGroupStateContext.Provider>
|
|
43
54
|
);
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
function DisclosureItem(props: {
|
|
47
|
-
|
|
57
|
+
function DisclosureItem(props: {
|
|
58
|
+
group: TDisclosureGroup;
|
|
59
|
+
icon?: React.ReactNode;
|
|
60
|
+
selectStateKey?: string;
|
|
61
|
+
selectIcon?: React.ReactNode;
|
|
62
|
+
}) {
|
|
63
|
+
const { icon, group, selectStateKey, selectIcon } = props;
|
|
48
64
|
|
|
49
65
|
const defaultId = useId();
|
|
50
|
-
const id = group.
|
|
66
|
+
const id = group.key || defaultId;
|
|
51
67
|
const groupState = useContext(DisclosureGroupStateContext);
|
|
52
68
|
const isExpanded = groupState?.expandedKeys.has(id) || false;
|
|
53
69
|
const state = useDisclosureState({
|
|
@@ -74,9 +90,9 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
74
90
|
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
75
91
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
76
92
|
|
|
77
|
-
const defaultTab = group.tabs?.[0]?.
|
|
78
|
-
const
|
|
79
|
-
const selectedTab = group.tabs?.find((tab) => tab.
|
|
93
|
+
const defaultTab = group.tabs?.[0]?.key || '';
|
|
94
|
+
const store = useSelectState(selectStateKey, defaultTab);
|
|
95
|
+
const selectedTab = group.tabs?.find((tab) => tab.key === store.key) || group.tabs?.[0];
|
|
80
96
|
|
|
81
97
|
return (
|
|
82
98
|
<div className="openapi-disclosure-group" aria-expanded={state.isExpanded}>
|
|
@@ -104,23 +120,25 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
104
120
|
{group.label}
|
|
105
121
|
</button>
|
|
106
122
|
{group.tabs ? (
|
|
107
|
-
<div
|
|
123
|
+
<div
|
|
124
|
+
className="openapi-disclosure-group-mediatype"
|
|
125
|
+
onClick={(e) => e.stopPropagation()}
|
|
126
|
+
>
|
|
108
127
|
{group.tabs?.length > 1 ? (
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
onChange={(event) => {
|
|
114
|
-
setSelectedTabKey(event.target.value);
|
|
128
|
+
<OpenAPISelect
|
|
129
|
+
icon={selectIcon}
|
|
130
|
+
stateKey={selectStateKey}
|
|
131
|
+
onSelectionChange={() => {
|
|
115
132
|
state.expand();
|
|
116
133
|
}}
|
|
134
|
+
items={group.tabs}
|
|
117
135
|
>
|
|
118
136
|
{group.tabs.map((tab) => (
|
|
119
|
-
<
|
|
137
|
+
<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
|
120
138
|
{tab.label}
|
|
121
|
-
</
|
|
139
|
+
</OpenAPISelectItem>
|
|
122
140
|
))}
|
|
123
|
-
</
|
|
141
|
+
</OpenAPISelect>
|
|
124
142
|
) : group.tabs[0]?.label ? (
|
|
125
143
|
<span>{group.tabs[0].label}</span>
|
|
126
144
|
) : null}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import type { OpenAPIContext, OpenAPIUniversalContext } from './context';
|
|
3
|
+
import { json2xml } from './json2xml';
|
|
4
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
5
|
+
import { t } from './translate';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Display an example.
|
|
9
|
+
*/
|
|
10
|
+
export function OpenAPIExample(props: {
|
|
11
|
+
example: OpenAPIV3.ExampleObject;
|
|
12
|
+
context: OpenAPIContext;
|
|
13
|
+
syntax: string;
|
|
14
|
+
}) {
|
|
15
|
+
const { example, context, syntax } = props;
|
|
16
|
+
const code = stringifyExample({ example, xml: syntax === 'xml' });
|
|
17
|
+
|
|
18
|
+
if (code === null) {
|
|
19
|
+
return <OpenAPIEmptyExample context={context} />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return context.renderCodeBlock({ code, syntax });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean }): string | null {
|
|
26
|
+
const { example, xml } = args;
|
|
27
|
+
|
|
28
|
+
if (!example.value) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof example.value === 'string') {
|
|
33
|
+
return example.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (xml) {
|
|
37
|
+
return json2xml(example.value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return stringifyOpenAPI(example.value, null, 2);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Empty response example.
|
|
45
|
+
*/
|
|
46
|
+
export function OpenAPIEmptyExample(props: {
|
|
47
|
+
context: OpenAPIUniversalContext;
|
|
48
|
+
}) {
|
|
49
|
+
const { context } = props;
|
|
50
|
+
return (
|
|
51
|
+
<pre className="openapi-example-empty">
|
|
52
|
+
<p>{t(context.translation, 'no_content')}</p>
|
|
53
|
+
</pre>
|
|
54
|
+
);
|
|
55
|
+
}
|