docusaurus-theme-openapi-docs 2.0.1 → 2.0.3
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/lib/theme/ApiExplorer/Body/index.js +8 -1
- package/lib/theme/ApiExplorer/Request/_Request.scss +4 -0
- package/lib/theme/ApiExplorer/index.js +4 -0
- package/lib/theme/ApiTabs/index.js +11 -3
- package/lib/theme/Markdown/index.js +37 -0
- package/lib/theme/OperationTabs/_OperationTabs.scss +68 -0
- package/lib/theme/OperationTabs/index.js +187 -0
- package/lib/theme/styles.scss +1 -0
- package/package.json +3 -3
- package/src/theme/ApiExplorer/Body/index.tsx +3 -1
- package/src/theme/ApiExplorer/Request/_Request.scss +4 -0
- package/src/theme/ApiExplorer/index.tsx +3 -0
- package/src/theme/ApiTabs/index.js +11 -3
- package/src/theme/Markdown/index.js +37 -0
- package/src/theme/OperationTabs/_OperationTabs.scss +68 -0
- package/src/theme/OperationTabs/index.js +187 -0
- package/src/theme/styles.scss +1 -0
- package/src/theme-openapi.d.ts +4 -0
|
@@ -23,6 +23,7 @@ const FormTextInput_1 = __importDefault(
|
|
|
23
23
|
);
|
|
24
24
|
const LiveEditor_1 = __importDefault(require("@theme/ApiExplorer/LiveEditor"));
|
|
25
25
|
const hooks_1 = require("@theme/ApiItem/hooks");
|
|
26
|
+
const Markdown_1 = __importDefault(require("@theme/Markdown"));
|
|
26
27
|
const SchemaTabs_1 = __importDefault(require("@theme/SchemaTabs"));
|
|
27
28
|
const TabItem_1 = __importDefault(require("@theme/TabItem"));
|
|
28
29
|
const xml_formatter_1 = __importDefault(require("xml-formatter"));
|
|
@@ -296,6 +297,10 @@ function Body({
|
|
|
296
297
|
react_1.default.createElement(
|
|
297
298
|
TabItem_1.default,
|
|
298
299
|
{ label: "Example", value: "example" },
|
|
300
|
+
example.summary &&
|
|
301
|
+
react_1.default.createElement(Markdown_1.default, {
|
|
302
|
+
children: example.summary,
|
|
303
|
+
}),
|
|
299
304
|
exampleBody &&
|
|
300
305
|
react_1.default.createElement(
|
|
301
306
|
LiveEditor_1.default,
|
|
@@ -337,7 +342,9 @@ function Body({
|
|
|
337
342
|
key: example.label,
|
|
338
343
|
},
|
|
339
344
|
example.summary &&
|
|
340
|
-
react_1.default.createElement(
|
|
345
|
+
react_1.default.createElement(Markdown_1.default, {
|
|
346
|
+
children: example.summary,
|
|
347
|
+
}),
|
|
341
348
|
example.body &&
|
|
342
349
|
react_1.default.createElement(
|
|
343
350
|
LiveEditor_1.default,
|
|
@@ -112,6 +112,10 @@
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
.openapi-security__summary-container {
|
|
116
|
+
background: var(--ifm-pre-background);
|
|
117
|
+
}
|
|
118
|
+
|
|
115
119
|
// Prevent auto zoom on mobile iOS devices when focusing on input elmenents
|
|
116
120
|
@media screen and (-webkit-min-device-pixel-ratio: 0) and (max-device-width: 1024px) {
|
|
117
121
|
.prism-code,
|
|
@@ -20,11 +20,15 @@ const CodeSnippets_1 = __importDefault(
|
|
|
20
20
|
);
|
|
21
21
|
const Request_1 = __importDefault(require("@theme/ApiExplorer/Request"));
|
|
22
22
|
const Response_1 = __importDefault(require("@theme/ApiExplorer/Response"));
|
|
23
|
+
const SecuritySchemes_1 = __importDefault(require("./SecuritySchemes"));
|
|
23
24
|
function ApiExplorer({ item, infoPath }) {
|
|
24
25
|
const postman = new postman_collection_1.default.Request(item.postman);
|
|
25
26
|
return react_1.default.createElement(
|
|
26
27
|
react_1.default.Fragment,
|
|
27
28
|
null,
|
|
29
|
+
react_1.default.createElement(SecuritySchemes_1.default, {
|
|
30
|
+
infoPath: infoPath,
|
|
31
|
+
}),
|
|
28
32
|
item.method !== "event" &&
|
|
29
33
|
react_1.default.createElement(CodeSnippets_1.default, {
|
|
30
34
|
postman: postman,
|
|
@@ -15,7 +15,15 @@ import useIsBrowser from "@docusaurus/useIsBrowser";
|
|
|
15
15
|
import Heading from "@theme/Heading";
|
|
16
16
|
import clsx from "clsx";
|
|
17
17
|
|
|
18
|
-
function TabList({
|
|
18
|
+
function TabList({
|
|
19
|
+
className,
|
|
20
|
+
block,
|
|
21
|
+
selectedValue,
|
|
22
|
+
selectValue,
|
|
23
|
+
tabValues,
|
|
24
|
+
label = "Responses",
|
|
25
|
+
id = "responses",
|
|
26
|
+
}) {
|
|
19
27
|
const tabRefs = [];
|
|
20
28
|
const { blockElementScrollPositionUntilNextRender } =
|
|
21
29
|
useScrollPositionBlocker();
|
|
@@ -84,8 +92,8 @@ function TabList({ className, block, selectedValue, selectValue, tabValues }) {
|
|
|
84
92
|
|
|
85
93
|
return (
|
|
86
94
|
<div className="openapi-tabs__response-header-section">
|
|
87
|
-
<Heading as="h2" id=
|
|
88
|
-
|
|
95
|
+
<Heading as="h2" id={id} className="openapi-tabs__response-header">
|
|
96
|
+
{label}
|
|
89
97
|
</Heading>
|
|
90
98
|
<div className="openapi-tabs__response-container">
|
|
91
99
|
{showTabArrows && (
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import CodeBlock from "@theme/CodeBlock";
|
|
11
|
+
import ReactMarkdown from "react-markdown";
|
|
12
|
+
import rehypeRaw from "rehype-raw";
|
|
13
|
+
|
|
14
|
+
function Markdown({ children }) {
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<ReactMarkdown
|
|
18
|
+
children={children}
|
|
19
|
+
rehypePlugins={[rehypeRaw]}
|
|
20
|
+
components={{
|
|
21
|
+
pre: "div",
|
|
22
|
+
code({ node, inline, className, children, ...props }) {
|
|
23
|
+
const match = /language-(\w+)/.exec(className || "");
|
|
24
|
+
if (inline) return <code>{children}</code>;
|
|
25
|
+
return !inline && match ? (
|
|
26
|
+
<CodeBlock className={className}>{children}</CodeBlock>
|
|
27
|
+
) : (
|
|
28
|
+
<CodeBlock>{children}</CodeBlock>
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default Markdown;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.openapi-tabs__operation-container {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
margin-top: 1rem;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.openapi-tabs__operation-item {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
padding: 0.35rem 0.7rem;
|
|
20
|
+
border: 1px solid transparent;
|
|
21
|
+
margin-top: 0 !important;
|
|
22
|
+
margin-right: 0.5rem;
|
|
23
|
+
font-weight: var(--ifm-font-weight-bold);
|
|
24
|
+
font-size: 12px;
|
|
25
|
+
white-space: nowrap;
|
|
26
|
+
transition: 300ms;
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
background-color: transparent;
|
|
30
|
+
border: 1px solid var(--ifm-toc-border-color);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.active {
|
|
34
|
+
border: 1px solid var(--ifm-tabs-color-active-border);
|
|
35
|
+
color: var(--ifm-tabs-color-active);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&:last-child {
|
|
39
|
+
margin-right: 0 !important;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.openapi-tabs__operation-list-container {
|
|
44
|
+
overflow-y: hidden;
|
|
45
|
+
overflow-x: scroll;
|
|
46
|
+
scroll-behavior: smooth;
|
|
47
|
+
|
|
48
|
+
&::-webkit-scrollbar {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.openapi-tabs__operation-schema-container {
|
|
54
|
+
max-width: 600px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media screen and (max-width: 500px) {
|
|
58
|
+
.operationTabsTopSection {
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
align-items: flex-start;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.operationTabsContainer {
|
|
64
|
+
width: 100%;
|
|
65
|
+
margin-top: var(--ifm-spacing-vertical);
|
|
66
|
+
padding: 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import React, { cloneElement, useEffect, useState, useRef } from "react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
useScrollPositionBlocker,
|
|
12
|
+
useTabs,
|
|
13
|
+
} from "@docusaurus/theme-common/internal";
|
|
14
|
+
import useIsBrowser from "@docusaurus/useIsBrowser";
|
|
15
|
+
import clsx from "clsx";
|
|
16
|
+
|
|
17
|
+
function TabList({ className, block, selectedValue, selectValue, tabValues }) {
|
|
18
|
+
const tabRefs = [];
|
|
19
|
+
const { blockElementScrollPositionUntilNextRender } =
|
|
20
|
+
useScrollPositionBlocker();
|
|
21
|
+
|
|
22
|
+
const handleTabChange = (event) => {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
const newTab = event.currentTarget;
|
|
25
|
+
const newTabIndex = tabRefs.indexOf(newTab);
|
|
26
|
+
const newTabValue = tabValues[newTabIndex].value;
|
|
27
|
+
// custom
|
|
28
|
+
if (newTabValue !== selectedValue) {
|
|
29
|
+
blockElementScrollPositionUntilNextRender(newTab);
|
|
30
|
+
selectValue(newTabValue);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleKeydown = (event) => {
|
|
35
|
+
let focusElement = null;
|
|
36
|
+
switch (event.key) {
|
|
37
|
+
case "Enter": {
|
|
38
|
+
handleTabChange(event);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "ArrowRight": {
|
|
42
|
+
const nextTab = tabRefs.indexOf(event.currentTarget) + 1;
|
|
43
|
+
focusElement = tabRefs[nextTab] ?? tabRefs[0];
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case "ArrowLeft": {
|
|
47
|
+
const prevTab = tabRefs.indexOf(event.currentTarget) - 1;
|
|
48
|
+
focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1];
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
default:
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
focusElement?.focus();
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const tabItemListContainerRef = useRef(null);
|
|
58
|
+
const [showTabArrows, setShowTabArrows] = useState(false);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
62
|
+
for (let entry of entries) {
|
|
63
|
+
if (entry.target.offsetWidth < entry.target.scrollWidth) {
|
|
64
|
+
setShowTabArrows(true);
|
|
65
|
+
} else {
|
|
66
|
+
setShowTabArrows(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
resizeObserver.observe(tabItemListContainerRef.current);
|
|
72
|
+
|
|
73
|
+
return () => {
|
|
74
|
+
resizeObserver.disconnect();
|
|
75
|
+
};
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
const handleRightClick = () => {
|
|
79
|
+
tabItemListContainerRef.current.scrollLeft += 90;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleLeftClick = () => {
|
|
83
|
+
tabItemListContainerRef.current.scrollLeft -= 90;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className="tabs__container">
|
|
88
|
+
<div className="openapi-tabs__operation-container">
|
|
89
|
+
{showTabArrows && (
|
|
90
|
+
<button
|
|
91
|
+
className={clsx("openapi-tabs__arrow", "left")}
|
|
92
|
+
onClick={handleLeftClick}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
<ul
|
|
96
|
+
ref={tabItemListContainerRef}
|
|
97
|
+
role="tablist"
|
|
98
|
+
aria-orientation="horizontal"
|
|
99
|
+
className={clsx(
|
|
100
|
+
"openapi-tabs__operation-list-container",
|
|
101
|
+
"tabs",
|
|
102
|
+
{
|
|
103
|
+
"tabs--block": block,
|
|
104
|
+
},
|
|
105
|
+
className
|
|
106
|
+
)}
|
|
107
|
+
>
|
|
108
|
+
{tabValues.map(({ value, label, attributes }) => {
|
|
109
|
+
return (
|
|
110
|
+
<li
|
|
111
|
+
role="tab"
|
|
112
|
+
tabIndex={selectedValue === value ? 0 : -1}
|
|
113
|
+
aria-selected={selectedValue === value}
|
|
114
|
+
key={value}
|
|
115
|
+
ref={(tabControl) => tabRefs.push(tabControl)}
|
|
116
|
+
onKeyDown={handleKeydown}
|
|
117
|
+
onFocus={handleTabChange}
|
|
118
|
+
onClick={(e) => handleTabChange(e)}
|
|
119
|
+
{...attributes}
|
|
120
|
+
className={clsx(
|
|
121
|
+
"tabs__item",
|
|
122
|
+
"openapi-tabs__operation-item",
|
|
123
|
+
attributes?.className,
|
|
124
|
+
{
|
|
125
|
+
active: selectedValue === value,
|
|
126
|
+
}
|
|
127
|
+
)}
|
|
128
|
+
>
|
|
129
|
+
{label ?? value}
|
|
130
|
+
</li>
|
|
131
|
+
);
|
|
132
|
+
})}
|
|
133
|
+
</ul>
|
|
134
|
+
{showTabArrows && (
|
|
135
|
+
<button
|
|
136
|
+
className={clsx("openapi-tabs__arrow", "right")}
|
|
137
|
+
onClick={handleRightClick}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
function TabContent({ lazy, children, selectedValue }) {
|
|
145
|
+
// eslint-disable-next-line no-param-reassign
|
|
146
|
+
children = Array.isArray(children) ? children : [children];
|
|
147
|
+
if (lazy) {
|
|
148
|
+
const selectedTabItem = children.find(
|
|
149
|
+
(tabItem) => tabItem.props.value === selectedValue
|
|
150
|
+
);
|
|
151
|
+
if (!selectedTabItem) {
|
|
152
|
+
// fail-safe or fail-fast? not sure what's best here
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return cloneElement(selectedTabItem, { className: "margin-top--md" });
|
|
156
|
+
}
|
|
157
|
+
return (
|
|
158
|
+
<div className="margin-top--md">
|
|
159
|
+
{children.map((tabItem, i) =>
|
|
160
|
+
cloneElement(tabItem, {
|
|
161
|
+
key: i,
|
|
162
|
+
hidden: tabItem.props.value !== selectedValue,
|
|
163
|
+
})
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
function TabsComponent(props) {
|
|
169
|
+
const tabs = useTabs(props);
|
|
170
|
+
return (
|
|
171
|
+
<div className="tabs-container">
|
|
172
|
+
<TabList {...props} {...tabs} />
|
|
173
|
+
<TabContent {...props} {...tabs} />
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
export default function OperationTabs(props) {
|
|
178
|
+
const isBrowser = useIsBrowser();
|
|
179
|
+
return (
|
|
180
|
+
<TabsComponent
|
|
181
|
+
// Remount tabs after hydration
|
|
182
|
+
// Temporary fix for https://github.com/facebook/docusaurus/issues/5653
|
|
183
|
+
key={String(isBrowser)}
|
|
184
|
+
{...props}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
package/lib/theme/styles.scss
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docusaurus-theme-openapi-docs",
|
|
3
3
|
"description": "OpenAPI theme for Docusaurus.",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"openapi",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"clsx": "^1.1.1",
|
|
44
44
|
"copy-text-to-clipboard": "^3.1.0",
|
|
45
45
|
"crypto-js": "^4.1.1",
|
|
46
|
-
"docusaurus-plugin-openapi-docs": "^2.0.
|
|
46
|
+
"docusaurus-plugin-openapi-docs": "^2.0.3",
|
|
47
47
|
"docusaurus-plugin-sass": "^0.2.3",
|
|
48
48
|
"file-saver": "^2.0.5",
|
|
49
49
|
"lodash": "^4.17.20",
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"engines": {
|
|
69
69
|
"node": ">=14"
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "f6641c0ce01e3247a06ef8ddde7bafbe749d4ed1"
|
|
72
72
|
}
|
|
@@ -14,6 +14,7 @@ import FormSelect from "@theme/ApiExplorer/FormSelect";
|
|
|
14
14
|
import FormTextInput from "@theme/ApiExplorer/FormTextInput";
|
|
15
15
|
import LiveApp from "@theme/ApiExplorer/LiveEditor";
|
|
16
16
|
import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
|
|
17
|
+
import Markdown from "@theme/Markdown";
|
|
17
18
|
import SchemaTabs from "@theme/SchemaTabs";
|
|
18
19
|
import TabItem from "@theme/TabItem";
|
|
19
20
|
import { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
@@ -303,6 +304,7 @@ function Body({
|
|
|
303
304
|
</TabItem>
|
|
304
305
|
{/* @ts-ignore */}
|
|
305
306
|
<TabItem label="Example" value="example">
|
|
307
|
+
{example.summary && <Markdown children={example.summary} />}
|
|
306
308
|
{exampleBody && (
|
|
307
309
|
<LiveApp
|
|
308
310
|
action={dispatch}
|
|
@@ -340,7 +342,7 @@ function Body({
|
|
|
340
342
|
value={example.label}
|
|
341
343
|
key={example.label}
|
|
342
344
|
>
|
|
343
|
-
{example.summary && <
|
|
345
|
+
{example.summary && <Markdown children={example.summary} />}
|
|
344
346
|
{example.body && (
|
|
345
347
|
<LiveApp action={dispatch} language={language}>
|
|
346
348
|
{example.body}
|
|
@@ -112,6 +112,10 @@
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
.openapi-security__summary-container {
|
|
116
|
+
background: var(--ifm-pre-background);
|
|
117
|
+
}
|
|
118
|
+
|
|
115
119
|
// Prevent auto zoom on mobile iOS devices when focusing on input elmenents
|
|
116
120
|
@media screen and (-webkit-min-device-pixel-ratio: 0) and (max-device-width: 1024px) {
|
|
117
121
|
.prism-code,
|
|
@@ -13,6 +13,8 @@ import Request from "@theme/ApiExplorer/Request";
|
|
|
13
13
|
import Response from "@theme/ApiExplorer/Response";
|
|
14
14
|
import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
|
|
15
15
|
|
|
16
|
+
import SecuritySchemes from "./SecuritySchemes";
|
|
17
|
+
|
|
16
18
|
function ApiExplorer({
|
|
17
19
|
item,
|
|
18
20
|
infoPath,
|
|
@@ -24,6 +26,7 @@ function ApiExplorer({
|
|
|
24
26
|
|
|
25
27
|
return (
|
|
26
28
|
<>
|
|
29
|
+
<SecuritySchemes infoPath={infoPath} />
|
|
27
30
|
{item.method !== "event" && (
|
|
28
31
|
<CodeSnippets
|
|
29
32
|
postman={postman}
|
|
@@ -15,7 +15,15 @@ import useIsBrowser from "@docusaurus/useIsBrowser";
|
|
|
15
15
|
import Heading from "@theme/Heading";
|
|
16
16
|
import clsx from "clsx";
|
|
17
17
|
|
|
18
|
-
function TabList({
|
|
18
|
+
function TabList({
|
|
19
|
+
className,
|
|
20
|
+
block,
|
|
21
|
+
selectedValue,
|
|
22
|
+
selectValue,
|
|
23
|
+
tabValues,
|
|
24
|
+
label = "Responses",
|
|
25
|
+
id = "responses",
|
|
26
|
+
}) {
|
|
19
27
|
const tabRefs = [];
|
|
20
28
|
const { blockElementScrollPositionUntilNextRender } =
|
|
21
29
|
useScrollPositionBlocker();
|
|
@@ -84,8 +92,8 @@ function TabList({ className, block, selectedValue, selectValue, tabValues }) {
|
|
|
84
92
|
|
|
85
93
|
return (
|
|
86
94
|
<div className="openapi-tabs__response-header-section">
|
|
87
|
-
<Heading as="h2" id=
|
|
88
|
-
|
|
95
|
+
<Heading as="h2" id={id} className="openapi-tabs__response-header">
|
|
96
|
+
{label}
|
|
89
97
|
</Heading>
|
|
90
98
|
<div className="openapi-tabs__response-container">
|
|
91
99
|
{showTabArrows && (
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import CodeBlock from "@theme/CodeBlock";
|
|
11
|
+
import ReactMarkdown from "react-markdown";
|
|
12
|
+
import rehypeRaw from "rehype-raw";
|
|
13
|
+
|
|
14
|
+
function Markdown({ children }) {
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<ReactMarkdown
|
|
18
|
+
children={children}
|
|
19
|
+
rehypePlugins={[rehypeRaw]}
|
|
20
|
+
components={{
|
|
21
|
+
pre: "div",
|
|
22
|
+
code({ node, inline, className, children, ...props }) {
|
|
23
|
+
const match = /language-(\w+)/.exec(className || "");
|
|
24
|
+
if (inline) return <code>{children}</code>;
|
|
25
|
+
return !inline && match ? (
|
|
26
|
+
<CodeBlock className={className}>{children}</CodeBlock>
|
|
27
|
+
) : (
|
|
28
|
+
<CodeBlock>{children}</CodeBlock>
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default Markdown;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.openapi-tabs__operation-container {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
margin-top: 1rem;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.openapi-tabs__operation-item {
|
|
16
|
+
display: flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
padding: 0.35rem 0.7rem;
|
|
20
|
+
border: 1px solid transparent;
|
|
21
|
+
margin-top: 0 !important;
|
|
22
|
+
margin-right: 0.5rem;
|
|
23
|
+
font-weight: var(--ifm-font-weight-bold);
|
|
24
|
+
font-size: 12px;
|
|
25
|
+
white-space: nowrap;
|
|
26
|
+
transition: 300ms;
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
background-color: transparent;
|
|
30
|
+
border: 1px solid var(--ifm-toc-border-color);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.active {
|
|
34
|
+
border: 1px solid var(--ifm-tabs-color-active-border);
|
|
35
|
+
color: var(--ifm-tabs-color-active);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&:last-child {
|
|
39
|
+
margin-right: 0 !important;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.openapi-tabs__operation-list-container {
|
|
44
|
+
overflow-y: hidden;
|
|
45
|
+
overflow-x: scroll;
|
|
46
|
+
scroll-behavior: smooth;
|
|
47
|
+
|
|
48
|
+
&::-webkit-scrollbar {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.openapi-tabs__operation-schema-container {
|
|
54
|
+
max-width: 600px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media screen and (max-width: 500px) {
|
|
58
|
+
.operationTabsTopSection {
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
align-items: flex-start;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.operationTabsContainer {
|
|
64
|
+
width: 100%;
|
|
65
|
+
margin-top: var(--ifm-spacing-vertical);
|
|
66
|
+
padding: 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import React, { cloneElement, useEffect, useState, useRef } from "react";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
useScrollPositionBlocker,
|
|
12
|
+
useTabs,
|
|
13
|
+
} from "@docusaurus/theme-common/internal";
|
|
14
|
+
import useIsBrowser from "@docusaurus/useIsBrowser";
|
|
15
|
+
import clsx from "clsx";
|
|
16
|
+
|
|
17
|
+
function TabList({ className, block, selectedValue, selectValue, tabValues }) {
|
|
18
|
+
const tabRefs = [];
|
|
19
|
+
const { blockElementScrollPositionUntilNextRender } =
|
|
20
|
+
useScrollPositionBlocker();
|
|
21
|
+
|
|
22
|
+
const handleTabChange = (event) => {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
const newTab = event.currentTarget;
|
|
25
|
+
const newTabIndex = tabRefs.indexOf(newTab);
|
|
26
|
+
const newTabValue = tabValues[newTabIndex].value;
|
|
27
|
+
// custom
|
|
28
|
+
if (newTabValue !== selectedValue) {
|
|
29
|
+
blockElementScrollPositionUntilNextRender(newTab);
|
|
30
|
+
selectValue(newTabValue);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const handleKeydown = (event) => {
|
|
35
|
+
let focusElement = null;
|
|
36
|
+
switch (event.key) {
|
|
37
|
+
case "Enter": {
|
|
38
|
+
handleTabChange(event);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "ArrowRight": {
|
|
42
|
+
const nextTab = tabRefs.indexOf(event.currentTarget) + 1;
|
|
43
|
+
focusElement = tabRefs[nextTab] ?? tabRefs[0];
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case "ArrowLeft": {
|
|
47
|
+
const prevTab = tabRefs.indexOf(event.currentTarget) - 1;
|
|
48
|
+
focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1];
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
default:
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
focusElement?.focus();
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const tabItemListContainerRef = useRef(null);
|
|
58
|
+
const [showTabArrows, setShowTabArrows] = useState(false);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
62
|
+
for (let entry of entries) {
|
|
63
|
+
if (entry.target.offsetWidth < entry.target.scrollWidth) {
|
|
64
|
+
setShowTabArrows(true);
|
|
65
|
+
} else {
|
|
66
|
+
setShowTabArrows(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
resizeObserver.observe(tabItemListContainerRef.current);
|
|
72
|
+
|
|
73
|
+
return () => {
|
|
74
|
+
resizeObserver.disconnect();
|
|
75
|
+
};
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
const handleRightClick = () => {
|
|
79
|
+
tabItemListContainerRef.current.scrollLeft += 90;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleLeftClick = () => {
|
|
83
|
+
tabItemListContainerRef.current.scrollLeft -= 90;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className="tabs__container">
|
|
88
|
+
<div className="openapi-tabs__operation-container">
|
|
89
|
+
{showTabArrows && (
|
|
90
|
+
<button
|
|
91
|
+
className={clsx("openapi-tabs__arrow", "left")}
|
|
92
|
+
onClick={handleLeftClick}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
<ul
|
|
96
|
+
ref={tabItemListContainerRef}
|
|
97
|
+
role="tablist"
|
|
98
|
+
aria-orientation="horizontal"
|
|
99
|
+
className={clsx(
|
|
100
|
+
"openapi-tabs__operation-list-container",
|
|
101
|
+
"tabs",
|
|
102
|
+
{
|
|
103
|
+
"tabs--block": block,
|
|
104
|
+
},
|
|
105
|
+
className
|
|
106
|
+
)}
|
|
107
|
+
>
|
|
108
|
+
{tabValues.map(({ value, label, attributes }) => {
|
|
109
|
+
return (
|
|
110
|
+
<li
|
|
111
|
+
role="tab"
|
|
112
|
+
tabIndex={selectedValue === value ? 0 : -1}
|
|
113
|
+
aria-selected={selectedValue === value}
|
|
114
|
+
key={value}
|
|
115
|
+
ref={(tabControl) => tabRefs.push(tabControl)}
|
|
116
|
+
onKeyDown={handleKeydown}
|
|
117
|
+
onFocus={handleTabChange}
|
|
118
|
+
onClick={(e) => handleTabChange(e)}
|
|
119
|
+
{...attributes}
|
|
120
|
+
className={clsx(
|
|
121
|
+
"tabs__item",
|
|
122
|
+
"openapi-tabs__operation-item",
|
|
123
|
+
attributes?.className,
|
|
124
|
+
{
|
|
125
|
+
active: selectedValue === value,
|
|
126
|
+
}
|
|
127
|
+
)}
|
|
128
|
+
>
|
|
129
|
+
{label ?? value}
|
|
130
|
+
</li>
|
|
131
|
+
);
|
|
132
|
+
})}
|
|
133
|
+
</ul>
|
|
134
|
+
{showTabArrows && (
|
|
135
|
+
<button
|
|
136
|
+
className={clsx("openapi-tabs__arrow", "right")}
|
|
137
|
+
onClick={handleRightClick}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
function TabContent({ lazy, children, selectedValue }) {
|
|
145
|
+
// eslint-disable-next-line no-param-reassign
|
|
146
|
+
children = Array.isArray(children) ? children : [children];
|
|
147
|
+
if (lazy) {
|
|
148
|
+
const selectedTabItem = children.find(
|
|
149
|
+
(tabItem) => tabItem.props.value === selectedValue
|
|
150
|
+
);
|
|
151
|
+
if (!selectedTabItem) {
|
|
152
|
+
// fail-safe or fail-fast? not sure what's best here
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return cloneElement(selectedTabItem, { className: "margin-top--md" });
|
|
156
|
+
}
|
|
157
|
+
return (
|
|
158
|
+
<div className="margin-top--md">
|
|
159
|
+
{children.map((tabItem, i) =>
|
|
160
|
+
cloneElement(tabItem, {
|
|
161
|
+
key: i,
|
|
162
|
+
hidden: tabItem.props.value !== selectedValue,
|
|
163
|
+
})
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
function TabsComponent(props) {
|
|
169
|
+
const tabs = useTabs(props);
|
|
170
|
+
return (
|
|
171
|
+
<div className="tabs-container">
|
|
172
|
+
<TabList {...props} {...tabs} />
|
|
173
|
+
<TabContent {...props} {...tabs} />
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
export default function OperationTabs(props) {
|
|
178
|
+
const isBrowser = useIsBrowser();
|
|
179
|
+
return (
|
|
180
|
+
<TabsComponent
|
|
181
|
+
// Remount tabs after hydration
|
|
182
|
+
// Temporary fix for https://github.com/facebook/docusaurus/issues/5653
|
|
183
|
+
key={String(isBrowser)}
|
|
184
|
+
{...props}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
package/src/theme/styles.scss
CHANGED
package/src/theme-openapi.d.ts
CHANGED
|
@@ -63,6 +63,10 @@ declare module "@theme/SchemaTabs" {
|
|
|
63
63
|
export default function SchemaTabs(props: any): JSX.Element;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
declare module "@theme/Markdown" {
|
|
67
|
+
export default function Markdown(props: any): JSX.Element;
|
|
68
|
+
}
|
|
69
|
+
|
|
66
70
|
declare module "@theme/ApiExplorer/Accept" {
|
|
67
71
|
export default function Accept(): JSX.Element;
|
|
68
72
|
}
|