@gitbook/react-openapi 1.0.4 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/InteractiveSection.jsx +10 -9
- package/dist/OpenAPICodeSample.jsx +3 -3
- package/dist/OpenAPIDisclosure.jsx +1 -0
- package/dist/OpenAPIDisclosureGroup.d.ts +1 -1
- package/dist/OpenAPIDisclosureGroup.jsx +1 -0
- package/dist/OpenAPIResponseExample.jsx +8 -8
- package/dist/OpenAPIResponses.jsx +3 -3
- package/dist/OpenAPISchema.jsx +1 -1
- package/dist/OpenAPISpec.jsx +3 -4
- package/dist/OpenAPITabs.jsx +51 -47
- package/dist/StaticSection.d.ts +10 -0
- package/dist/StaticSection.jsx +23 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/useSyncedTabsGlobalState.d.ts +10 -1
- package/dist/useSyncedTabsGlobalState.js +19 -15
- package/package.json +1 -1
- package/src/InteractiveSection.tsx +10 -18
- package/src/OpenAPICodeSample.tsx +3 -3
- package/src/OpenAPIDisclosure.tsx +1 -0
- package/src/OpenAPIDisclosureGroup.tsx +11 -9
- package/src/OpenAPIResponseExample.tsx +8 -15
- package/src/OpenAPIResponses.tsx +3 -3
- package/src/OpenAPISchema.tsx +2 -2
- package/src/OpenAPISpec.tsx +3 -5
- package/src/OpenAPITabs.tsx +52 -63
- package/src/StaticSection.tsx +59 -0
- package/src/useSyncedTabsGlobalState.ts +33 -21
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
type Key = string | number;
|
|
2
|
+
type TabState = {
|
|
3
|
+
tabKey: Key | null;
|
|
4
|
+
};
|
|
5
|
+
type TabActions = {
|
|
6
|
+
setTabKey: (tab: Key | null) => void;
|
|
7
|
+
};
|
|
8
|
+
type TabStore = TabState & TabActions;
|
|
9
|
+
export declare const getOrCreateTabStoreByKey: (storeKey: string, initialKey?: Key) => import("zustand").StoreApi<TabStore>;
|
|
10
|
+
export {};
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
2
|
+
import { createStore } from 'zustand';
|
|
3
|
+
var createTabStore = function (initialTab) {
|
|
4
|
+
return createStore()(function (set) { return ({
|
|
5
|
+
tabKey: initialTab !== null && initialTab !== void 0 ? initialTab : null,
|
|
6
|
+
setTabKey: function (tabKey) {
|
|
7
|
+
set(function () { return ({ tabKey: tabKey }); });
|
|
8
|
+
},
|
|
9
|
+
}); });
|
|
10
|
+
};
|
|
11
|
+
var defaultTabStores = new Map();
|
|
12
|
+
var createTabStoreFactory = function (stores) {
|
|
13
|
+
return function (storeKey, initialKey) {
|
|
14
|
+
if (!stores.has(storeKey)) {
|
|
15
|
+
stores.set(storeKey, createTabStore(initialKey));
|
|
16
|
+
}
|
|
17
|
+
return stores.get(storeKey);
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export var getOrCreateTabStoreByKey = createTabStoreFactory(defaultTabStores);
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import clsx from 'clsx';
|
|
|
4
4
|
import { useRef, useState } from 'react';
|
|
5
5
|
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
|
|
6
6
|
import { useDisclosureState } from 'react-stately';
|
|
7
|
+
import { Section, SectionBody, SectionHeader, SectionHeaderContent } from './StaticSection';
|
|
7
8
|
|
|
8
9
|
interface InteractiveSectionTab {
|
|
9
10
|
key: string;
|
|
@@ -63,7 +64,7 @@ export function InteractiveSection(props: {
|
|
|
63
64
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
64
65
|
|
|
65
66
|
return (
|
|
66
|
-
<
|
|
67
|
+
<Section
|
|
67
68
|
id={id}
|
|
68
69
|
className={clsx(
|
|
69
70
|
'openapi-section',
|
|
@@ -73,20 +74,15 @@ export function InteractiveSection(props: {
|
|
|
73
74
|
)}
|
|
74
75
|
>
|
|
75
76
|
{header ? (
|
|
76
|
-
<
|
|
77
|
+
<SectionHeader
|
|
77
78
|
onClick={() => {
|
|
78
79
|
if (toggeable) {
|
|
79
80
|
state.toggle();
|
|
80
81
|
}
|
|
81
82
|
}}
|
|
82
|
-
className={
|
|
83
|
+
className={className}
|
|
83
84
|
>
|
|
84
|
-
<
|
|
85
|
-
className={clsx(
|
|
86
|
-
'openapi-section-header-content',
|
|
87
|
-
`${className}-header-content`
|
|
88
|
-
)}
|
|
89
|
-
>
|
|
85
|
+
<SectionHeaderContent className={className}>
|
|
90
86
|
{(children || selectedTab?.body) && toggeable ? (
|
|
91
87
|
<button
|
|
92
88
|
{...mergeProps(buttonProps, focusProps)}
|
|
@@ -102,7 +98,7 @@ export function InteractiveSection(props: {
|
|
|
102
98
|
</button>
|
|
103
99
|
) : null}
|
|
104
100
|
{header}
|
|
105
|
-
</
|
|
101
|
+
</SectionHeaderContent>
|
|
106
102
|
<div
|
|
107
103
|
className={clsx(
|
|
108
104
|
'openapi-section-header-controls',
|
|
@@ -133,23 +129,19 @@ export function InteractiveSection(props: {
|
|
|
133
129
|
</select>
|
|
134
130
|
) : null}
|
|
135
131
|
</div>
|
|
136
|
-
</
|
|
132
|
+
</SectionHeader>
|
|
137
133
|
) : null}
|
|
138
134
|
{(!toggeable || state.isExpanded) && (children || selectedTab?.body) ? (
|
|
139
|
-
<
|
|
140
|
-
ref={panelRef}
|
|
141
|
-
{...panelProps}
|
|
142
|
-
className={clsx('openapi-section-body', `${className}-body`)}
|
|
143
|
-
>
|
|
135
|
+
<SectionBody ref={panelRef} {...panelProps} className={className}>
|
|
144
136
|
{children}
|
|
145
137
|
{selectedTab?.body}
|
|
146
|
-
</
|
|
138
|
+
</SectionBody>
|
|
147
139
|
) : null}
|
|
148
140
|
{overlay ? (
|
|
149
141
|
<div className={clsx('openapi-section-overlay', `${className}-overlay`)}>
|
|
150
142
|
{overlay}
|
|
151
143
|
</div>
|
|
152
144
|
) : null}
|
|
153
|
-
</
|
|
145
|
+
</Section>
|
|
154
146
|
);
|
|
155
147
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { InteractiveSection } from './InteractiveSection';
|
|
2
1
|
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
|
|
2
|
+
import { StaticSection } from './StaticSection';
|
|
3
3
|
import { type CodeSampleInput, codeSampleGenerators } from './code-samples';
|
|
4
4
|
import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
|
|
5
5
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
@@ -117,9 +117,9 @@ export function OpenAPICodeSample(props: {
|
|
|
117
117
|
|
|
118
118
|
return (
|
|
119
119
|
<OpenAPITabs stateKey={createStateKey('codesample')} items={samples}>
|
|
120
|
-
<
|
|
120
|
+
<StaticSection header={<OpenAPITabsList />} className="openapi-codesample">
|
|
121
121
|
<OpenAPITabsPanels />
|
|
122
|
-
</
|
|
122
|
+
</StaticSection>
|
|
123
123
|
</OpenAPITabs>
|
|
124
124
|
);
|
|
125
125
|
}
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useRef, useState } from 'react';
|
|
4
|
+
import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
|
|
5
|
+
import {
|
|
6
|
+
type DisclosureGroupProps,
|
|
7
|
+
type DisclosureGroupState,
|
|
8
|
+
useDisclosureGroupState,
|
|
9
|
+
useDisclosureState,
|
|
10
|
+
} from 'react-stately';
|
|
11
|
+
|
|
1
12
|
interface Props {
|
|
2
13
|
groups: TDisclosureGroup[];
|
|
3
14
|
icon?: React.ReactNode;
|
|
@@ -13,15 +24,6 @@ type TDisclosureGroup = {
|
|
|
13
24
|
}[];
|
|
14
25
|
};
|
|
15
26
|
|
|
16
|
-
import { createContext, useContext, useRef, useState } from 'react';
|
|
17
|
-
import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
|
|
18
|
-
import {
|
|
19
|
-
type DisclosureGroupProps,
|
|
20
|
-
type DisclosureGroupState,
|
|
21
|
-
useDisclosureGroupState,
|
|
22
|
-
useDisclosureState,
|
|
23
|
-
} from 'react-stately';
|
|
24
|
-
|
|
25
27
|
const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(null);
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
-
import { InteractiveSection } from './InteractiveSection';
|
|
3
2
|
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
|
|
3
|
+
import { StaticSection } from './StaticSection';
|
|
4
4
|
import { generateSchemaExample } from './generateSchemaExample';
|
|
5
5
|
import { json2xml } from './json2xml';
|
|
6
6
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
@@ -84,9 +84,9 @@ export function OpenAPIResponseExample(props: {
|
|
|
84
84
|
|
|
85
85
|
return (
|
|
86
86
|
<OpenAPITabs stateKey={createStateKey('response-example')} items={tabs}>
|
|
87
|
-
<
|
|
87
|
+
<StaticSection header={<OpenAPITabsList />} className="openapi-response-example">
|
|
88
88
|
<OpenAPITabsPanels />
|
|
89
|
-
</
|
|
89
|
+
</StaticSection>
|
|
90
90
|
</OpenAPITabs>
|
|
91
91
|
);
|
|
92
92
|
}
|
|
@@ -134,12 +134,9 @@ function OpenAPIResponse(props: {
|
|
|
134
134
|
|
|
135
135
|
return (
|
|
136
136
|
<OpenAPITabs stateKey={createStateKey('response-media-types')} items={tabs}>
|
|
137
|
-
<
|
|
138
|
-
header={<OpenAPITabsList />}
|
|
139
|
-
className="openapi-response-media-types"
|
|
140
|
-
>
|
|
137
|
+
<StaticSection header={<OpenAPITabsList />} className="openapi-response-media-types">
|
|
141
138
|
<OpenAPITabsPanels />
|
|
142
|
-
</
|
|
139
|
+
</StaticSection>
|
|
143
140
|
</OpenAPITabs>
|
|
144
141
|
);
|
|
145
142
|
}
|
|
@@ -173,23 +170,19 @@ function OpenAPIResponseMediaType(props: {
|
|
|
173
170
|
key: example.key,
|
|
174
171
|
label: example.example.summary || example.key,
|
|
175
172
|
body: (
|
|
176
|
-
<OpenAPIExample
|
|
177
|
-
example={firstExample.example}
|
|
178
|
-
context={props.context}
|
|
179
|
-
syntax={syntax}
|
|
180
|
-
/>
|
|
173
|
+
<OpenAPIExample example={example.example} context={props.context} syntax={syntax} />
|
|
181
174
|
),
|
|
182
175
|
};
|
|
183
176
|
});
|
|
184
177
|
|
|
185
178
|
return (
|
|
186
179
|
<OpenAPITabs stateKey={createStateKey('response-media-type-examples')} items={tabs}>
|
|
187
|
-
<
|
|
180
|
+
<StaticSection
|
|
188
181
|
header={<OpenAPITabsList />}
|
|
189
182
|
className="openapi-response-media-type-examples"
|
|
190
183
|
>
|
|
191
184
|
<OpenAPITabsPanels />
|
|
192
|
-
</
|
|
185
|
+
</StaticSection>
|
|
193
186
|
</OpenAPITabs>
|
|
194
187
|
);
|
|
195
188
|
}
|
package/src/OpenAPIResponses.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
|
2
|
-
import { InteractiveSection } from './InteractiveSection';
|
|
3
2
|
import { Markdown } from './Markdown';
|
|
4
3
|
import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
|
|
5
4
|
import { OpenAPIResponse } from './OpenAPIResponse';
|
|
5
|
+
import { StaticSection } from './StaticSection';
|
|
6
6
|
import type { OpenAPIClientContext } from './types';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -15,7 +15,7 @@ export function OpenAPIResponses(props: {
|
|
|
15
15
|
const { responses, context } = props;
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
|
-
<
|
|
18
|
+
<StaticSection header="Responses" className="openapi-responses">
|
|
19
19
|
<OpenAPIDisclosureGroup
|
|
20
20
|
allowsMultipleExpanded
|
|
21
21
|
icon={context.icons.chevronRight}
|
|
@@ -58,6 +58,6 @@ export function OpenAPIResponses(props: {
|
|
|
58
58
|
}
|
|
59
59
|
)}
|
|
60
60
|
/>
|
|
61
|
-
</
|
|
61
|
+
</StaticSection>
|
|
62
62
|
);
|
|
63
63
|
}
|
package/src/OpenAPISchema.tsx
CHANGED
|
@@ -119,9 +119,9 @@ export function OpenAPISchemaProperties(props: {
|
|
|
119
119
|
|
|
120
120
|
return (
|
|
121
121
|
<div id={id} className="openapi-schema-properties">
|
|
122
|
-
{properties.map((property) => (
|
|
122
|
+
{properties.map((property, index) => (
|
|
123
123
|
<OpenAPISchemaProperty
|
|
124
|
-
key={
|
|
124
|
+
key={index}
|
|
125
125
|
circularRefs={circularRefs}
|
|
126
126
|
{...property}
|
|
127
127
|
context={context}
|
package/src/OpenAPISpec.tsx
CHANGED
|
@@ -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
|
-
<
|
|
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
|
-
</
|
|
42
|
+
</StaticSection>
|
|
45
43
|
);
|
|
46
44
|
})}
|
|
47
45
|
|
package/src/OpenAPITabs.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { type Key, Tab, TabList, TabPanel, Tabs, type TabsProps } from 'react-aria-components';
|
|
5
|
-
import {
|
|
5
|
+
import { useEventCallback } from 'usehooks-ts';
|
|
6
6
|
import { Markdown } from './Markdown';
|
|
7
|
-
import {
|
|
7
|
+
import { getOrCreateTabStoreByKey } from './useSyncedTabsGlobalState';
|
|
8
8
|
|
|
9
9
|
export type TabItem = {
|
|
10
10
|
key: Key;
|
|
@@ -15,7 +15,7 @@ export type TabItem = {
|
|
|
15
15
|
|
|
16
16
|
type OpenAPITabsContextData = {
|
|
17
17
|
items: TabItem[];
|
|
18
|
-
selectedTab: TabItem;
|
|
18
|
+
selectedTab: TabItem | null;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const OpenAPITabsContext = createContext<OpenAPITabsContextData | null>(null);
|
|
@@ -35,68 +35,54 @@ export function OpenAPITabs(
|
|
|
35
35
|
props: React.PropsWithChildren<TabsProps & { items: TabItem[]; stateKey?: string }>
|
|
36
36
|
) {
|
|
37
37
|
const { children, items, stateKey } = props;
|
|
38
|
-
const [
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const [selectedTabKey, setSelectedTabKey] = useState(() => {
|
|
46
|
-
if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
|
|
47
|
-
const tabFromState = syncedTabs.get(stateKey);
|
|
48
|
-
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
|
+
}
|
|
49
45
|
}
|
|
50
|
-
return items[0]?.key;
|
|
46
|
+
return items[0]?.key ?? null;
|
|
51
47
|
});
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
setSelectedTabKey(key);
|
|
56
|
-
if (stateKey) {
|
|
57
|
-
const tab = items.find((item) => item.key === key);
|
|
58
|
-
|
|
59
|
-
if (!tab) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
setSyncedTabs((state) => {
|
|
64
|
-
const newState = new Map(state);
|
|
65
|
-
newState.set(stateKey, tab);
|
|
66
|
-
return newState;
|
|
67
|
-
});
|
|
48
|
+
const selectTab = useEventCallback((key: Key | null) => {
|
|
49
|
+
if (!key || key === tabKey) {
|
|
50
|
+
return;
|
|
68
51
|
}
|
|
69
|
-
|
|
70
|
-
|
|
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);
|
|
71
60
|
useEffect(() => {
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (!items.some((item) => item.key === tabFromState?.key)) {
|
|
76
|
-
return setSelectedTab(defaultTab);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (tabFromState && tabFromState?.key !== selectedTab?.key) {
|
|
80
|
-
const tabFromItems = items.find((item) => item.key === tabFromState.key);
|
|
81
|
-
|
|
82
|
-
if (!tabFromItems) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
setSelectedTab(tabFromItems);
|
|
87
|
-
}
|
|
61
|
+
if (!stateKey) {
|
|
62
|
+
return undefined;
|
|
88
63
|
}
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
}, []);
|
|
91
73
|
const contextValue = useMemo(() => ({ items, selectedTab }), [items, selectedTab]);
|
|
92
|
-
|
|
93
74
|
return (
|
|
94
75
|
<OpenAPITabsContext.Provider value={contextValue}>
|
|
95
76
|
<Tabs
|
|
96
|
-
ref={ref}
|
|
97
77
|
className="openapi-tabs"
|
|
98
|
-
onSelectionChange={
|
|
99
|
-
|
|
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" />
|
|
@@ -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
|
+
}
|
|
@@ -1,23 +1,35 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
3
|
+
import { createStore } from 'zustand';
|
|
4
|
+
|
|
5
|
+
type Key = string | number;
|
|
6
|
+
|
|
7
|
+
type TabState = {
|
|
8
|
+
tabKey: Key | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type TabActions = { setTabKey: (tab: Key | null) => void };
|
|
12
|
+
|
|
13
|
+
type TabStore = TabState & TabActions;
|
|
14
|
+
|
|
15
|
+
const createTabStore = (initialTab?: Key) => {
|
|
16
|
+
return createStore<TabStore>()((set) => ({
|
|
17
|
+
tabKey: initialTab ?? null,
|
|
18
|
+
setTabKey: (tabKey) => {
|
|
19
|
+
set(() => ({ tabKey }));
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const defaultTabStores = new Map<string, ReturnType<typeof createTabStore>>();
|
|
25
|
+
|
|
26
|
+
const createTabStoreFactory = (stores: typeof defaultTabStores) => {
|
|
27
|
+
return (storeKey: string, initialKey?: Key) => {
|
|
28
|
+
if (!stores.has(storeKey)) {
|
|
29
|
+
stores.set(storeKey, createTabStore(initialKey));
|
|
30
|
+
}
|
|
31
|
+
return stores.get(storeKey)!;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const getOrCreateTabStoreByKey = createTabStoreFactory(defaultTabStores);
|