-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathOpenAPICodeSampleInteractive.tsx
156 lines (142 loc) · 4.9 KB
/
OpenAPICodeSampleInteractive.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
'use client';
import clsx from 'clsx';
import { useCallback } from 'react';
import { useStore } from 'zustand';
import type { MediaTypeRenderer } from './OpenAPICodeSample';
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
type MediaTypeState = {
mediaType: string;
setMediaType: (mediaType: string) => void;
};
function useMediaTypeState(
data: { method: string; path: string },
defaultKey: string
): MediaTypeState {
const { method, path } = data;
const store = useStore(getOrCreateStoreByKey(`media-type-${method}-${path}`, defaultKey));
if (typeof store.key !== 'string') {
throw new Error('Media type key is not a string');
}
return {
mediaType: store.key,
setMediaType: useCallback((index: string) => store.setKey(index), [store.setKey]),
};
}
function useMediaTypeSampleIndexState(data: { method: string; path: string }, mediaType: string) {
const { method, path } = data;
const store = useStore(
getOrCreateStoreByKey(`media-type-sample-${mediaType}-${method}-${path}`, 0)
);
if (typeof store.key !== 'number') {
throw new Error('Example key is not a number');
}
return {
index: store.key,
setIndex: useCallback((index: number) => store.setKey(index), [store.setKey]),
};
}
export function OpenAPIMediaTypeExamplesSelector(props: {
method: string;
path: string;
renderers: MediaTypeRenderer[];
}) {
const { method, path, renderers } = props;
if (!renderers[0]) {
throw new Error('No renderers provided');
}
const state = useMediaTypeState({ method, path }, renderers[0].mediaType);
const selected = renderers.find((r) => r.mediaType === state.mediaType) || renderers[0];
return (
<div className="openapi-codesample-selectors">
<MediaTypeSelector state={state} renderers={renderers} />
<ExamplesSelector method={method} path={path} renderer={selected} />
</div>
);
}
function MediaTypeSelector(props: {
state: MediaTypeState;
renderers: MediaTypeRenderer[];
}) {
const { renderers, state } = props;
if (renderers.length < 2) {
return null;
}
const items = renderers.map((renderer) => ({
key: renderer.mediaType,
label: renderer.mediaType,
}));
return (
<OpenAPISelect
className={clsx('openapi-select')}
selectedKey={state.mediaType}
items={renderers.map((renderer) => ({
key: renderer.mediaType,
label: renderer.mediaType,
}))}
onSelectionChange={(e) => state.setMediaType(String(e))}
placement="bottom start"
>
{items.map((item) => (
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
{item.label}
</OpenAPISelectItem>
))}
</OpenAPISelect>
);
}
function ExamplesSelector(props: {
method: string;
path: string;
renderer: MediaTypeRenderer;
}) {
const { method, path, renderer } = props;
const state = useMediaTypeSampleIndexState({ method, path }, renderer.mediaType);
if (renderer.examples.length < 2) {
return null;
}
const items = renderer.examples.map((example, index) => ({
key: index,
label: example.example.summary || `Example ${index + 1}`,
}));
return (
<OpenAPISelect
items={items}
selectedKey={state.index}
onSelectionChange={(e) => state.setIndex(Number(e))}
placement="bottom start"
>
{items.map((item) => (
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
{item.label}
</OpenAPISelectItem>
))}
</OpenAPISelect>
);
}
export function OpenAPIMediaTypeExamplesBody(props: {
method: string;
path: string;
renderers: MediaTypeRenderer[];
}) {
const { renderers, method, path } = props;
if (!renderers[0]) {
throw new Error('No renderers provided');
}
const mediaTypeState = useMediaTypeState({ method, path }, renderers[0].mediaType);
const selected =
renderers.find((r) => r.mediaType === mediaTypeState.mediaType) ?? renderers[0];
if (selected.examples.length === 0) {
return selected.element;
}
return <ExamplesBody method={method} path={path} renderer={selected} />;
}
function ExamplesBody(props: { method: string; path: string; renderer: MediaTypeRenderer }) {
const { method, path, renderer } = props;
const exampleState = useMediaTypeSampleIndexState({ method, path }, renderer.mediaType);
const example = renderer.examples[exampleState.index] ?? renderer.examples[0];
if (!example) {
throw new Error(`No example found for index ${exampleState.index}`);
}
return example.element;
}