@gitbook/react-openapi 1.5.2 → 1.5.4
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 +31 -0
- package/dist/InteractiveSection.js +2 -2
- package/dist/OpenAPICodeSample.js +8 -5
- package/dist/OpenAPICodeSampleInteractive.js +1 -5
- package/dist/OpenAPICopyButton.js +12 -7
- package/dist/OpenAPIDisclosure.js +4 -6
- package/dist/OpenAPIDisclosureGroup.js +21 -7
- package/dist/OpenAPIPath.js +12 -38
- package/dist/OpenAPIPathItem.js +22 -0
- package/dist/OpenAPIPathMultipleServers.js +43 -0
- package/dist/OpenAPIRequiredScopes.js +67 -0
- package/dist/OpenAPISchema.js +187 -79
- package/dist/OpenAPISecurities.js +17 -43
- package/dist/OpenAPISelect.js +6 -6
- package/dist/OpenAPISpec.js +1 -1
- package/dist/OpenAPITooltip.js +23 -0
- package/dist/ScalarApiButton.js +5 -2
- package/dist/code-samples.js +33 -3
- package/dist/context.d.ts +3 -0
- package/dist/formatPath.js +25 -0
- package/dist/generateSchemaExample.js +20 -3
- package/dist/getOrCreateDisclosureStoreByKey.js +31 -0
- package/dist/resolveOpenAPIOperation.js +5 -2
- package/dist/translate.js +2 -2
- package/dist/translations/de.js +2 -0
- package/dist/translations/en.d.ts +2 -0
- package/dist/translations/en.js +2 -0
- package/dist/translations/es.js +2 -0
- package/dist/translations/fr.js +2 -0
- package/dist/translations/index.d.ts +18 -0
- package/dist/translations/ja.js +2 -0
- package/dist/translations/nl.js +2 -0
- package/dist/translations/no.js +2 -0
- package/dist/translations/pt-br.js +2 -0
- package/dist/translations/zh.js +2 -0
- package/dist/types.d.ts +1 -0
- package/dist/util/tryit-prefill.js +4 -1
- package/dist/utils.js +8 -6
- package/package.json +23 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @gitbook/react-openapi
|
|
2
2
|
|
|
3
|
+
## 1.5.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4766092: Fix missing properties in allOf/oneOf
|
|
8
|
+
- 8761cee: Enhance discriminator handling in OpenAPISchema
|
|
9
|
+
- 461e15f: Add x-gitbook-prefix and x-gitbook-token-placeholder for OpenAPI security scheme
|
|
10
|
+
- 3e40b4d: Fix OpenAPI basic auth placeholder
|
|
11
|
+
- 87d68ea: Fix OpenAPI oneOf/allOf merge
|
|
12
|
+
- 344842f: Improve OpenAPI circular references
|
|
13
|
+
- Updated dependencies [461e15f]
|
|
14
|
+
- @gitbook/openapi-parser@3.0.6
|
|
15
|
+
|
|
16
|
+
## 1.5.3
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- b4a021a: Add heredoc support for cURL JSON body
|
|
21
|
+
- a512c90: Re-arrange OpenAPI Scopes for OAuth2
|
|
22
|
+
- df1966d: Bump Scalar
|
|
23
|
+
- b45feaf: Disable OpenAPI "Try it" when no servers are defined
|
|
24
|
+
- 10995e0: Use NPM Trusted publishing for publishing the package.
|
|
25
|
+
- f9f8011: Add alt text support to card covers
|
|
26
|
+
- 8ce7322: Add OpenAPI servers selection
|
|
27
|
+
- 2c3066e: Improve OAuth2 scopes handling in OpenAPI
|
|
28
|
+
- Updated dependencies [df1966d]
|
|
29
|
+
- Updated dependencies [10995e0]
|
|
30
|
+
- Updated dependencies [10995e0]
|
|
31
|
+
- @gitbook/openapi-parser@3.0.5
|
|
32
|
+
- @gitbook/expr@1.2.4
|
|
33
|
+
|
|
3
34
|
## 1.5.2
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
import { Section, SectionBody, SectionHeader, SectionHeaderContent } from "./StaticSection.js";
|
|
5
5
|
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from "./OpenAPISelect.js";
|
|
6
|
-
import { useDisclosureState } from "./node_modules/react-stately/dist/import.js";
|
|
7
6
|
import clsx from "classnames";
|
|
8
7
|
import { useRef } from "react";
|
|
9
8
|
import { mergeProps, useButton, useDisclosure, useFocusRing } from "react-aria";
|
|
9
|
+
import { useDisclosureState } from "react-stately";
|
|
10
10
|
|
|
11
11
|
//#region src/InteractiveSection.tsx
|
|
12
12
|
/**
|
|
@@ -37,7 +37,7 @@ function InteractiveSection(props) {
|
|
|
37
37
|
<div className={clsx("openapi-section-header-controls", `${className}-header-controls`)} onClick={(event) => {
|
|
38
38
|
event.stopPropagation();
|
|
39
39
|
}}>
|
|
40
|
-
{tabs.length > 0 ? <OpenAPISelect stateKey={stateKey} items={tabs}
|
|
40
|
+
{tabs.length > 0 ? <OpenAPISelect stateKey={stateKey} items={tabs} onChange={() => {
|
|
41
41
|
state.expand();
|
|
42
42
|
}} icon={selectIcon} placement="bottom end">
|
|
43
43
|
{tabs.map((tab) => <OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
|
@@ -132,10 +132,10 @@ function OpenAPICodeSampleFooter(props) {
|
|
|
132
132
|
const hideTryItPanel = data["x-hideTryItPanel"] || data.operation["x-hideTryItPanel"];
|
|
133
133
|
const hasMultipleMediaTypes = renderers.length > 1 || renderers.some((renderer) => renderer.examples.length > 0);
|
|
134
134
|
if (hideTryItPanel && !hasMultipleMediaTypes) return null;
|
|
135
|
-
if (!validateHttpMethod(method)) return null;
|
|
135
|
+
if (!validateHttpMethod(method) || !hasMultipleMediaTypes && servers.length === 0) return null;
|
|
136
136
|
return <div className="openapi-codesample-footer">
|
|
137
137
|
{hasMultipleMediaTypes ? <OpenAPIMediaTypeExamplesSelector method={data.method} path={data.path} renderers={renderers} selectIcon={context.icons.chevronDown} blockKey={context.blockKey} /> : <span />}
|
|
138
|
-
{!hideTryItPanel && <ScalarApiButton context={getOpenAPIClientContext(context)} method={method} path={path} securities={securities} servers={servers} specUrl={specUrl} />}
|
|
138
|
+
{!hideTryItPanel && servers.length > 0 && <ScalarApiButton context={getOpenAPIClientContext(context)} method={method} path={path} securities={securities} servers={servers} specUrl={specUrl} />}
|
|
139
139
|
</div>;
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
@@ -175,25 +175,28 @@ function getSecurityHeaders(args) {
|
|
|
175
175
|
let scheme = security.scheme;
|
|
176
176
|
const format = resolvePrefillCodePlaceholderFromSecurityScheme({
|
|
177
177
|
security,
|
|
178
|
-
defaultPlaceholderValue: scheme?.includes("basic") ? "username:password" : "YOUR_SECRET_TOKEN"
|
|
178
|
+
defaultPlaceholderValue: scheme?.toLowerCase()?.includes("basic") ? "username:password" : "YOUR_SECRET_TOKEN"
|
|
179
179
|
});
|
|
180
180
|
if (scheme?.includes("bearer")) scheme = "Bearer";
|
|
181
181
|
else if (scheme?.includes("basic")) scheme = "Basic";
|
|
182
182
|
else if (scheme?.includes("token")) scheme = "Token";
|
|
183
|
+
else scheme = scheme ?? "";
|
|
183
184
|
headers.Authorization = `${scheme} ${format}`;
|
|
184
185
|
break;
|
|
185
186
|
}
|
|
186
187
|
case "apiKey": {
|
|
187
188
|
if (security.in !== "header") break;
|
|
188
189
|
const name = security.name ?? "Authorization";
|
|
189
|
-
|
|
190
|
+
const placeholder = resolvePrefillCodePlaceholderFromSecurityScheme({
|
|
190
191
|
security,
|
|
191
192
|
defaultPlaceholderValue: "YOUR_API_KEY"
|
|
192
193
|
});
|
|
194
|
+
const prefix = security["x-gitbook-prefix"];
|
|
195
|
+
headers[name] = prefix ? `${prefix} ${placeholder}` : placeholder;
|
|
193
196
|
break;
|
|
194
197
|
}
|
|
195
198
|
case "oauth2":
|
|
196
|
-
headers.Authorization =
|
|
199
|
+
headers.Authorization = `${security["x-gitbook-prefix"] ?? "Bearer"} ${resolvePrefillCodePlaceholderFromSecurityScheme({
|
|
197
200
|
security,
|
|
198
201
|
defaultPlaceholderValue: "YOUR_OAUTH2_TOKEN"
|
|
199
202
|
})}`;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
import { createStateKey } from "./utils.js";
|
|
5
5
|
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from "./OpenAPISelect.js";
|
|
6
|
-
import clsx from "classnames";
|
|
7
6
|
|
|
8
7
|
//#region src/OpenAPICodeSampleInteractive.tsx
|
|
9
8
|
function OpenAPIMediaTypeExamplesSelector(props) {
|
|
@@ -24,10 +23,7 @@ function MediaTypeSelector(props) {
|
|
|
24
23
|
key: renderer.mediaType,
|
|
25
24
|
label: renderer.mediaType
|
|
26
25
|
}));
|
|
27
|
-
return <OpenAPISelect
|
|
28
|
-
key: renderer.mediaType,
|
|
29
|
-
label: renderer.mediaType
|
|
30
|
-
}))} icon={selectIcon} stateKey={stateKey} placement="bottom start">
|
|
26
|
+
return <OpenAPISelect items={items} icon={selectIcon} stateKey={stateKey} placement="bottom start">
|
|
31
27
|
{items.map((item) => <OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
32
28
|
{item.label}
|
|
33
29
|
</OpenAPISelectItem>)}
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import { t } from "./translate.js";
|
|
5
|
+
import { OpenAPITooltip } from "./OpenAPITooltip.js";
|
|
6
|
+
import clsx from "classnames";
|
|
5
7
|
import { useState } from "react";
|
|
6
|
-
import { Button
|
|
8
|
+
import { Button } from "react-aria-components";
|
|
7
9
|
|
|
8
10
|
//#region src/OpenAPICopyButton.tsx
|
|
9
11
|
function OpenAPICopyButton(props) {
|
|
@@ -21,18 +23,21 @@ function OpenAPICopyButton(props) {
|
|
|
21
23
|
}, 2e3);
|
|
22
24
|
});
|
|
23
25
|
};
|
|
24
|
-
return <
|
|
26
|
+
return <OpenAPITooltip isDisabled={!withTooltip} isOpen={isOpen} onOpenChange={setIsOpen}>
|
|
25
27
|
<Button type="button" preventFocusOnPress onPress={(e) => {
|
|
26
28
|
handleCopy();
|
|
27
29
|
onPress?.(e);
|
|
28
|
-
}} className={
|
|
30
|
+
}} className={clsx("openapi-copy-button", className)} {...props}>
|
|
29
31
|
{children}
|
|
30
32
|
</Button>
|
|
31
33
|
|
|
32
|
-
<
|
|
33
|
-
{copied ?
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
<OpenAPITooltip.Content isOpen={isOpen} onOpenChange={setIsOpen}>
|
|
35
|
+
{copied ? <>
|
|
36
|
+
{context.icons.check}
|
|
37
|
+
{t(context.translation, "copied")}
|
|
38
|
+
</> : label || t(context.translation, "copy_to_clipboard")}
|
|
39
|
+
</OpenAPITooltip.Content>
|
|
40
|
+
</OpenAPITooltip>;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
//#endregion
|
|
@@ -10,19 +10,17 @@ import { Button, Disclosure, DisclosurePanel } from "react-aria-components";
|
|
|
10
10
|
* Display an interactive OpenAPI disclosure.
|
|
11
11
|
*/
|
|
12
12
|
function OpenAPIDisclosure(props) {
|
|
13
|
-
const { icon, header, label, children, className } = props;
|
|
14
|
-
const [isExpanded, setIsExpanded] = useState(
|
|
13
|
+
const { icon, header, label, children, className, defaultExpanded = false } = props;
|
|
14
|
+
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
|
15
15
|
return <Disclosure className={clsx("openapi-disclosure", className)} isExpanded={isExpanded} onExpandedChange={setIsExpanded}>
|
|
16
16
|
<Button slot="trigger" className="openapi-disclosure-trigger" style={({ isFocusVisible }) => ({ outline: isFocusVisible ? "2px solid rgb(var(--primary-color-500) / 0.4)" : "none" })}>
|
|
17
17
|
{header}
|
|
18
18
|
<div className="openapi-disclosure-trigger-label">
|
|
19
|
-
<span>{typeof label === "function" ? label(isExpanded) : label}</span>
|
|
19
|
+
{label ? <span>{typeof label === "function" ? label(isExpanded) : label}</span> : null}
|
|
20
20
|
{icon}
|
|
21
21
|
</div>
|
|
22
22
|
</Button>
|
|
23
|
-
<DisclosurePanel className="openapi-disclosure-panel">
|
|
24
|
-
{isExpanded ? children : null}
|
|
25
|
-
</DisclosurePanel>
|
|
23
|
+
{isExpanded ? <DisclosurePanel className="openapi-disclosure-panel">{children}</DisclosurePanel> : null}
|
|
26
24
|
</Disclosure>;
|
|
27
25
|
}
|
|
28
26
|
|
|
@@ -2,24 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from "./OpenAPISelect.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getOrCreateDisclosureStoreByKey } from "./getOrCreateDisclosureStoreByKey.js";
|
|
6
|
+
import clsx from "classnames";
|
|
6
7
|
import { createContext, useContext, useRef } from "react";
|
|
8
|
+
import { useStore } from "zustand";
|
|
7
9
|
import { mergeProps, useButton, useDisclosure, useFocusRing, useId as useId$1 } from "react-aria";
|
|
10
|
+
import { useDisclosureGroupState, useDisclosureState } from "react-stately";
|
|
8
11
|
|
|
9
12
|
//#region src/OpenAPIDisclosureGroup.tsx
|
|
10
13
|
const DisclosureGroupStateContext = createContext(null);
|
|
14
|
+
function useDisclosureGroupStore(stateKey = "disclosure-group", initialKeys) {
|
|
15
|
+
return useStore(getOrCreateDisclosureStoreByKey(stateKey, initialKeys));
|
|
16
|
+
}
|
|
11
17
|
/**
|
|
12
18
|
* Display an interactive OpenAPI disclosure group.
|
|
13
19
|
*/
|
|
14
20
|
function OpenAPIDisclosureGroup(props) {
|
|
15
|
-
const { icon, groups, selectStateKey, selectIcon } = props;
|
|
16
|
-
const
|
|
21
|
+
const { icon, groups, selectStateKey, stateKey, selectIcon, className, expandedKeys, defaultExpandedKeys, onExpandedChange } = props;
|
|
22
|
+
const { expandedKeys: storeExpandedKeys, setExpandedKeys } = useDisclosureGroupStore(stateKey, expandedKeys || defaultExpandedKeys ? new Set(expandedKeys || defaultExpandedKeys) : void 0);
|
|
23
|
+
const state = useDisclosureGroupState({
|
|
24
|
+
...props,
|
|
25
|
+
expandedKeys: storeExpandedKeys,
|
|
26
|
+
onExpandedChange: (keys) => {
|
|
27
|
+
setExpandedKeys(keys);
|
|
28
|
+
onExpandedChange?.(keys);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
17
31
|
return <DisclosureGroupStateContext.Provider value={state}>
|
|
18
|
-
{groups.map((group) => <DisclosureItem selectStateKey={selectStateKey} selectIcon={selectIcon} icon={icon} key={group.key} group={group} />)}
|
|
32
|
+
{groups.map((group) => <DisclosureItem className={className} selectStateKey={selectStateKey} selectIcon={selectIcon} icon={icon} key={group.key} group={group} />)}
|
|
19
33
|
</DisclosureGroupStateContext.Provider>;
|
|
20
34
|
}
|
|
21
35
|
function DisclosureItem(props) {
|
|
22
|
-
const { icon, group, selectStateKey, selectIcon } = props;
|
|
36
|
+
const { icon, group, selectStateKey, selectIcon, className } = props;
|
|
23
37
|
const defaultId = useId$1();
|
|
24
38
|
const id = group.key || defaultId;
|
|
25
39
|
const groupState = useContext(DisclosureGroupStateContext);
|
|
@@ -42,7 +56,7 @@ function DisclosureItem(props) {
|
|
|
42
56
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
43
57
|
const store = useSelectState(selectStateKey, group.tabs?.[0]?.key || "");
|
|
44
58
|
const selectedTab = group.tabs?.find((tab) => tab.key === store.key) || group.tabs?.[0];
|
|
45
|
-
return <div className="openapi-disclosure-group" aria-expanded={state.isExpanded}>
|
|
59
|
+
return <div className={clsx("openapi-disclosure-group", className)} aria-expanded={state.isExpanded}>
|
|
46
60
|
<div slot="trigger" ref={triggerRef} {...mergeProps(buttonProps, focusProps)} aria-disabled={isDisabled} style={{ outline: isFocusVisible ? "2px solid rgb(var(--primary-color-500)/0.4)" : "none" }} className="openapi-disclosure-group-trigger">
|
|
47
61
|
<div className="openapi-disclosure-group-icon">
|
|
48
62
|
{icon || <svg viewBox="0 0 24 24" className="openapi-disclosure-group-icon">
|
|
@@ -54,7 +68,7 @@ function DisclosureItem(props) {
|
|
|
54
68
|
{group.label}
|
|
55
69
|
|
|
56
70
|
{group.tabs ? <div className="openapi-disclosure-group-mediatype" onClick={(e) => e.stopPropagation()}>
|
|
57
|
-
{group.tabs?.length > 1 ? <OpenAPISelect icon={selectIcon} stateKey={selectStateKey}
|
|
71
|
+
{group.tabs?.length > 1 ? <OpenAPISelect icon={selectIcon} stateKey={selectStateKey} onChange={() => {
|
|
58
72
|
state.expand();
|
|
59
73
|
}} items={group.tabs} placement="bottom end">
|
|
60
74
|
{group.tabs.map((tab) => <OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
package/dist/OpenAPIPath.js
CHANGED
|
@@ -1,50 +1,24 @@
|
|
|
1
|
-
import { OpenAPICopyButton } from "./OpenAPICopyButton.js";
|
|
2
1
|
import { getOpenAPIClientContext } from "./context.js";
|
|
2
|
+
import { OpenAPIPathItem } from "./OpenAPIPathItem.js";
|
|
3
|
+
import { formatPath } from "./formatPath.js";
|
|
3
4
|
import { getDefaultServerURL } from "./util/server.js";
|
|
5
|
+
import { OpenAPIPathMultipleServers } from "./OpenAPIPathMultipleServers.js";
|
|
4
6
|
|
|
5
7
|
//#region src/OpenAPIPath.tsx
|
|
6
8
|
/**
|
|
7
9
|
* Display the path of an operation.
|
|
8
10
|
*/
|
|
9
11
|
function OpenAPIPath(props) {
|
|
10
|
-
const { data,
|
|
11
|
-
const {
|
|
12
|
-
const
|
|
12
|
+
const { data, withServer = true, context } = props;
|
|
13
|
+
const { path } = data;
|
|
14
|
+
const clientContext = getOpenAPIClientContext(context);
|
|
15
|
+
if (withServer && data.servers.length > 1) return <OpenAPIPathMultipleServers {...props} context={clientContext} />;
|
|
13
16
|
const formattedPath = formatPath(path);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
})();
|
|
20
|
-
return <div className="openapi-path">
|
|
21
|
-
<div className={`openapi-method openapi-method-${method}`}>{method}</div>
|
|
22
|
-
|
|
23
|
-
<OpenAPICopyButton value={`${withServer ? server : ""}${path}`} className="openapi-path-title" data-deprecated={operation.deprecated} isDisabled={!canCopy} context={getOpenAPIClientContext(context)}>
|
|
24
|
-
{element}
|
|
25
|
-
</OpenAPICopyButton>
|
|
26
|
-
</div>;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Format the path by wrapping placeholders in <span> tags.
|
|
30
|
-
*/
|
|
31
|
-
function formatPath(path) {
|
|
32
|
-
const regex = /\{\s*(\w+)\s*\}|:\w+/g;
|
|
33
|
-
const parts = [];
|
|
34
|
-
let lastIndex = 0;
|
|
35
|
-
path.replace(regex, (match, _, offset) => {
|
|
36
|
-
if (offset > lastIndex) parts.push(path.slice(lastIndex, offset));
|
|
37
|
-
parts.push(<span key={`offset-${offset}`} className="openapi-path-variable">
|
|
38
|
-
{match}
|
|
39
|
-
</span>);
|
|
40
|
-
lastIndex = offset + match.length;
|
|
41
|
-
return match;
|
|
42
|
-
});
|
|
43
|
-
if (lastIndex < path.length) parts.push(path.slice(lastIndex));
|
|
44
|
-
return parts.map((part, index) => {
|
|
45
|
-
if (typeof part === "string") return <span key={`part-${index}`}>{part}</span>;
|
|
46
|
-
return part;
|
|
47
|
-
});
|
|
17
|
+
const defaultServer = getDefaultServerURL(data.servers);
|
|
18
|
+
return <OpenAPIPathItem {...props} value={`${defaultServer}${path}`} context={clientContext}>
|
|
19
|
+
{withServer ? <span className="openapi-path-server">{defaultServer}</span> : null}
|
|
20
|
+
{formattedPath}
|
|
21
|
+
</OpenAPIPathItem>;
|
|
48
22
|
}
|
|
49
23
|
|
|
50
24
|
//#endregion
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { OpenAPICopyButton } from "./OpenAPICopyButton.js";
|
|
2
|
+
|
|
3
|
+
//#region src/OpenAPIPathItem.tsx
|
|
4
|
+
function OpenAPIPathItem(props) {
|
|
5
|
+
const { value, canCopy = true, context, children, data, copyType = "children" } = props;
|
|
6
|
+
const { operation, method } = data;
|
|
7
|
+
const title = <span className="openapi-path-title">{children}</span>;
|
|
8
|
+
return <div className="openapi-path">
|
|
9
|
+
<div className={`openapi-method openapi-method-${method}`}>{method}</div>
|
|
10
|
+
{canCopy && value ? copyType === "children" ? <OpenAPICopyButton value={value} data-deprecated={operation.deprecated} isDisabled={!canCopy} context={context} className="openapi-path-copy-button">
|
|
11
|
+
{title}
|
|
12
|
+
</OpenAPICopyButton> : <>
|
|
13
|
+
{title}
|
|
14
|
+
<OpenAPICopyButton value={value} data-deprecated={operation.deprecated} isDisabled={!canCopy} context={context} className="openapi-path-copy-button openapi-path-copy-button-icon">
|
|
15
|
+
{context.icons.copy}
|
|
16
|
+
</OpenAPICopyButton>
|
|
17
|
+
</> : title}
|
|
18
|
+
</div>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
export { OpenAPIPathItem };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { OpenAPITooltip } from "./OpenAPITooltip.js";
|
|
5
|
+
import { createStateKey } from "./utils.js";
|
|
6
|
+
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from "./OpenAPISelect.js";
|
|
7
|
+
import { OpenAPIPathItem } from "./OpenAPIPathItem.js";
|
|
8
|
+
import { formatPath } from "./formatPath.js";
|
|
9
|
+
import { getDefaultServerURL } from "./util/server.js";
|
|
10
|
+
import { Text } from "react-aria-components";
|
|
11
|
+
|
|
12
|
+
//#region src/OpenAPIPathMultipleServers.tsx
|
|
13
|
+
const serversStateKey = createStateKey("servers");
|
|
14
|
+
/**
|
|
15
|
+
* Display the path of an operation.
|
|
16
|
+
*/
|
|
17
|
+
function OpenAPIPathMultipleServers(props) {
|
|
18
|
+
const { data, withServer = true, context } = props;
|
|
19
|
+
const { path, servers } = data;
|
|
20
|
+
const defaultServer = getDefaultServerURL(servers);
|
|
21
|
+
const { key, setKey } = useSelectState(serversStateKey, defaultServer);
|
|
22
|
+
const formattedPath = formatPath(path);
|
|
23
|
+
const items = servers.filter((server) => !!server.url).map((server) => ({
|
|
24
|
+
key: server.url,
|
|
25
|
+
label: server.url,
|
|
26
|
+
description: server.description
|
|
27
|
+
}));
|
|
28
|
+
return <OpenAPIPathItem copyType="button" {...props} value={`${withServer ? key : ""}${path}`} context={context}>
|
|
29
|
+
{withServer ? <OpenAPITooltip>
|
|
30
|
+
<OpenAPISelect className="openapi-select openapi-select-unstyled" items={items} stateKey={serversStateKey} placement="bottom start" icon={context.icons.chevronDown} defaultValue={defaultServer} onChange={setKey}>
|
|
31
|
+
{items.map((item) => <OpenAPISelectItem textValue={item.label} key={item.key} id={item.key} value={item} className="openapi-select-item-column">
|
|
32
|
+
<Text slot="label">{item.label}</Text>
|
|
33
|
+
{item.description ? <Text slot="description">{item.description}</Text> : null}
|
|
34
|
+
</OpenAPISelectItem>)}
|
|
35
|
+
</OpenAPISelect>
|
|
36
|
+
<OpenAPITooltip.Content>Click to select a server</OpenAPITooltip.Content>
|
|
37
|
+
</OpenAPITooltip> : null}
|
|
38
|
+
{formattedPath}
|
|
39
|
+
</OpenAPIPathItem>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { OpenAPIPathMultipleServers };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { t } from "./translate.js";
|
|
5
|
+
import { OpenAPICopyButton } from "./OpenAPICopyButton.js";
|
|
6
|
+
import { useSelectState } from "./OpenAPISelect.js";
|
|
7
|
+
import { OpenAPIDisclosureGroup } from "./OpenAPIDisclosureGroup.js";
|
|
8
|
+
|
|
9
|
+
//#region src/OpenAPIRequiredScopes.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Present securities authorization that can be used for this operation.
|
|
12
|
+
*/
|
|
13
|
+
function OpenAPIRequiredScopes(props) {
|
|
14
|
+
const { securities, stateKey, context } = props;
|
|
15
|
+
const { key: selectedKey } = useSelectState(stateKey, securities[0]?.key);
|
|
16
|
+
const selectedSecurity = securities.find((security) => security.key === selectedKey);
|
|
17
|
+
if (!selectedSecurity) return null;
|
|
18
|
+
const scopes = selectedSecurity.schemes.flatMap((scheme) => {
|
|
19
|
+
return scheme.scopes ?? [];
|
|
20
|
+
});
|
|
21
|
+
if (!scopes.length) return null;
|
|
22
|
+
return <OpenAPIDisclosureGroup className="openapi-required-scopes" icon={context.icons.chevronRight} stateKey="required-scopes" defaultExpandedKeys={["required-scopes"]} groups={[{
|
|
23
|
+
key: "required-scopes",
|
|
24
|
+
label: <div className="openapi-required-scopes-header">
|
|
25
|
+
{context.icons.lock}
|
|
26
|
+
<span>{t(context.translation, "required_scopes")}</span>
|
|
27
|
+
</div>,
|
|
28
|
+
tabs: [{
|
|
29
|
+
key: "scopes",
|
|
30
|
+
label: "",
|
|
31
|
+
body: <OpenAPISchemaScopes scopes={scopes} context={context} />
|
|
32
|
+
}]
|
|
33
|
+
}]} />;
|
|
34
|
+
}
|
|
35
|
+
function OpenAPISchemaScopes(props) {
|
|
36
|
+
const { scopes, context, isOAuth2 } = props;
|
|
37
|
+
return <div className="openapi-securities-scopes openapi-markdown">
|
|
38
|
+
<div className="openapi-required-scopes-description">
|
|
39
|
+
{t(context.translation, isOAuth2 ? "available_scopes" : "required_scopes_description")}
|
|
40
|
+
</div>
|
|
41
|
+
<ul>
|
|
42
|
+
{scopes.map((scope) => <OpenAPIScopeItem key={scope[0]} scope={scope} context={context} />)}
|
|
43
|
+
</ul>
|
|
44
|
+
</div>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Display a scope item. Either a key-value pair or a single string.
|
|
48
|
+
*/
|
|
49
|
+
function OpenAPIScopeItem(props) {
|
|
50
|
+
const { scope, context } = props;
|
|
51
|
+
return <li>
|
|
52
|
+
<OpenAPIScopeItemKey name={scope[0]} context={context} />
|
|
53
|
+
{scope[1] ? <span>: {scope[1]}</span> : null}
|
|
54
|
+
</li>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Displays the scope name within a copyable button.
|
|
58
|
+
*/
|
|
59
|
+
function OpenAPIScopeItemKey(props) {
|
|
60
|
+
const { name, context } = props;
|
|
61
|
+
return <OpenAPICopyButton value={name} context={context} withTooltip>
|
|
62
|
+
<code>{name}</code>
|
|
63
|
+
</OpenAPICopyButton>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { OpenAPIRequiredScopes, OpenAPISchemaScopes };
|