Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
add snippet + rework UI/UX
Browse files- components/editor/main/endpoint.tsx +9 -4
- components/editor/main/index.tsx +2 -1
- components/editor/main/parameter.tsx +10 -5
- components/editor/main/request.tsx +102 -80
- components/editor/main/snippet/curl.tsx +1 -0
- components/editor/main/snippet/index.tsx +125 -0
- components/editor/main/tabs.tsx +66 -0
- components/input/input.tsx +16 -6
- components/input/toggle.tsx +22 -5
- utils/datas/api_collections.ts +6 -0
- utils/type.ts +2 -1
components/editor/main/endpoint.tsx
CHANGED
@@ -28,17 +28,22 @@ export const Endpoint = ({
|
|
28 |
};
|
29 |
|
30 |
return (
|
31 |
-
<div className="bg-slate-
|
32 |
-
<div className="bg-slate-950
|
33 |
<div className="text-white text-sm flex items-center justify-start gap-2 w-full">
|
34 |
<Method method={method} />
|
35 |
-
<div className="flex items-center justify-start gap-1">
|
36 |
{path_formatted.map((p, i) => {
|
37 |
return p.editable ? (
|
38 |
<Parameter
|
39 |
key={i}
|
40 |
value={p.content}
|
41 |
-
onChange={(value) =>
|
|
|
|
|
|
|
|
|
|
|
42 |
/>
|
43 |
) : (
|
44 |
<p key={i} className="text-slate-300">
|
|
|
28 |
};
|
29 |
|
30 |
return (
|
31 |
+
<div className="bg-slate-950/40 w-full px-4 xl:px-6 pt-5">
|
32 |
+
<div className="bg-slate-950 p-3 rounded-lg flex items-center justify-between">
|
33 |
<div className="text-white text-sm flex items-center justify-start gap-2 w-full">
|
34 |
<Method method={method} />
|
35 |
+
<div className="flex items-center justify-start gap-1 flex-wrap">
|
36 |
{path_formatted.map((p, i) => {
|
37 |
return p.editable ? (
|
38 |
<Parameter
|
39 |
key={i}
|
40 |
value={p.content}
|
41 |
+
onChange={(value, currentValue) =>
|
42 |
+
handleChange(
|
43 |
+
value,
|
44 |
+
currentValue === p.key ? p.key : currentValue
|
45 |
+
)
|
46 |
+
}
|
47 |
/>
|
48 |
) : (
|
49 |
<p key={i} className="text-slate-300">
|
components/editor/main/index.tsx
CHANGED
@@ -47,7 +47,6 @@ export const EditorMain = ({ endpoint }: { endpoint: ApiRoute }) => {
|
|
47 |
<div className="h-full grid grid-cols-1 xl:grid-cols-3">
|
48 |
<Request
|
49 |
parameters={formattedParameters}
|
50 |
-
body={endpoint?.body}
|
51 |
formattedBody={formattedBody}
|
52 |
onParamsChange={(k: string, v: string | boolean) => {
|
53 |
setFormattedParameters({
|
@@ -55,6 +54,8 @@ export const EditorMain = ({ endpoint }: { endpoint: ApiRoute }) => {
|
|
55 |
[k]: v,
|
56 |
});
|
57 |
}}
|
|
|
|
|
58 |
onBodyChange={(b: Options) => setFormattedBody(b)}
|
59 |
>
|
60 |
<Endpoint
|
|
|
47 |
<div className="h-full grid grid-cols-1 xl:grid-cols-3">
|
48 |
<Request
|
49 |
parameters={formattedParameters}
|
|
|
50 |
formattedBody={formattedBody}
|
51 |
onParamsChange={(k: string, v: string | boolean) => {
|
52 |
setFormattedParameters({
|
|
|
54 |
[k]: v,
|
55 |
});
|
56 |
}}
|
57 |
+
endpoint={endpoint}
|
58 |
+
formattedEndpoint={formattedEndpoint}
|
59 |
onBodyChange={(b: Options) => setFormattedBody(b)}
|
60 |
>
|
61 |
<Endpoint
|
components/editor/main/parameter.tsx
CHANGED
@@ -1,19 +1,24 @@
|
|
1 |
-
import {
|
2 |
-
import
|
3 |
-
import { on } from "events";
|
4 |
|
5 |
interface Props {
|
6 |
value: string;
|
7 |
-
onChange: (value: string) => void;
|
8 |
}
|
9 |
export const Parameter: React.FC<Props> = ({ value, onChange }) => {
|
10 |
const [state, setState] = useState(value);
|
|
|
|
|
|
|
11 |
|
12 |
return (
|
13 |
<input
|
14 |
type="text"
|
15 |
className="bg-indigo-600 !text-white px-1.5 rounded-md outline-none bg-opacity-80 max-w-[80px] text-center truncate"
|
16 |
-
onBlur={() =>
|
|
|
|
|
|
|
17 |
value={state}
|
18 |
onChange={(e) => setState(e.target.value)}
|
19 |
/>
|
|
|
1 |
+
import { useState } from "react";
|
2 |
+
import { useUpdateEffect } from "react-use";
|
|
|
3 |
|
4 |
interface Props {
|
5 |
value: string;
|
6 |
+
onChange: (value: string, currentValue: string) => void;
|
7 |
}
|
8 |
export const Parameter: React.FC<Props> = ({ value, onChange }) => {
|
9 |
const [state, setState] = useState(value);
|
10 |
+
const [previousValue, setPreviousValue] = useState<string | undefined>(
|
11 |
+
undefined
|
12 |
+
);
|
13 |
|
14 |
return (
|
15 |
<input
|
16 |
type="text"
|
17 |
className="bg-indigo-600 !text-white px-1.5 rounded-md outline-none bg-opacity-80 max-w-[80px] text-center truncate"
|
18 |
+
onBlur={() => {
|
19 |
+
onChange(state, previousValue ?? `{${value}}`);
|
20 |
+
setPreviousValue(state as string);
|
21 |
+
}}
|
22 |
value={state}
|
23 |
onChange={(e) => setState(e.target.value)}
|
24 |
/>
|
components/editor/main/request.tsx
CHANGED
@@ -5,24 +5,32 @@ import { Options } from "redaxios";
|
|
5 |
import { Toggle } from "@/components/input/toggle";
|
6 |
import { TextInput } from "@/components/input/input";
|
7 |
import { usePersistentState } from "@/utils/usePersistentState";
|
8 |
-
import { Body } from "@/utils/type";
|
9 |
import { useUpdateEffect } from "react-use";
|
|
|
|
|
10 |
|
11 |
export const Request = ({
|
12 |
parameters,
|
13 |
-
body,
|
14 |
formattedBody,
|
|
|
15 |
onBodyChange,
|
|
|
16 |
children,
|
17 |
onParamsChange,
|
18 |
}: {
|
19 |
-
parameters: any
|
20 |
children: React.ReactElement;
|
21 |
-
body: Array<Body> | undefined;
|
22 |
formattedBody: Options | undefined;
|
|
|
|
|
23 |
onBodyChange: (o: Options) => void;
|
24 |
onParamsChange: (key: string, value: string | boolean) => void;
|
25 |
}) => {
|
|
|
|
|
|
|
|
|
26 |
const [headers, setHeaders] = usePersistentState("headers", {
|
27 |
Authorization: "",
|
28 |
});
|
@@ -32,89 +40,103 @@ export const Request = ({
|
|
32 |
useUpdateEffect(() => onBodyChange(bodyForm), [bodyForm]);
|
33 |
|
34 |
return (
|
35 |
-
<div className="h-full bg-slate-900
|
36 |
{children}
|
37 |
-
{
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
label={key}
|
|
|
|
|
53 |
onChange={(e) => onParamsChange(key, e)}
|
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 |
label={b.key}
|
84 |
-
|
85 |
onChange={(e) => setBodyForm({ ...bodyForm, [b.key]: e })}
|
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 |
-
setHeaders({ ...headers, Authorization })
|
116 |
-
}
|
117 |
-
/>
|
118 |
</div>
|
119 |
</div>
|
120 |
);
|
|
|
5 |
import { Toggle } from "@/components/input/toggle";
|
6 |
import { TextInput } from "@/components/input/input";
|
7 |
import { usePersistentState } from "@/utils/usePersistentState";
|
8 |
+
import { ApiRoute, Body } from "@/utils/type";
|
9 |
import { useUpdateEffect } from "react-use";
|
10 |
+
import { Snippet } from "./snippet";
|
11 |
+
import { Tabs } from "./tabs";
|
12 |
|
13 |
export const Request = ({
|
14 |
parameters,
|
|
|
15 |
formattedBody,
|
16 |
+
formattedEndpoint,
|
17 |
onBodyChange,
|
18 |
+
endpoint,
|
19 |
children,
|
20 |
onParamsChange,
|
21 |
}: {
|
22 |
+
parameters: Record<string, any>;
|
23 |
children: React.ReactElement;
|
|
|
24 |
formattedBody: Options | undefined;
|
25 |
+
endpoint: ApiRoute;
|
26 |
+
formattedEndpoint: string;
|
27 |
onBodyChange: (o: Options) => void;
|
28 |
onParamsChange: (key: string, value: string | boolean) => void;
|
29 |
}) => {
|
30 |
+
const [tab, setTab] = useState<"headers" | "parameters" | "body" | "snippet">(
|
31 |
+
endpoint?.parameters ? "parameters" : endpoint?.body ? "body" : "headers"
|
32 |
+
);
|
33 |
+
|
34 |
const [headers, setHeaders] = usePersistentState("headers", {
|
35 |
Authorization: "",
|
36 |
});
|
|
|
40 |
useUpdateEffect(() => onBodyChange(bodyForm), [bodyForm]);
|
41 |
|
42 |
return (
|
43 |
+
<div className="h-full bg-slate-900 pb-5 overflow-auto">
|
44 |
{children}
|
45 |
+
<Tabs active={tab} setActive={setTab} endpoint={endpoint} />
|
46 |
+
<div className="px-4 xl:px-6">
|
47 |
+
{tab === "parameters" && parameters && (
|
48 |
+
<div className="mt-6 grid grid-cols-2 gap-6 w-full">
|
49 |
+
<p className="text-slate-200 uppercase text-xs font-semibold col-span-2">
|
50 |
+
Optional parameters
|
51 |
+
</p>
|
52 |
+
{parameters &&
|
53 |
+
Object.entries(parameters).map(([key, value]) => (
|
54 |
+
<div
|
55 |
+
key={key}
|
56 |
+
className="flex items-center justify-between gap-2"
|
57 |
+
>
|
58 |
+
{typeof value === "boolean" ? (
|
59 |
+
<div>
|
60 |
+
<Toggle
|
61 |
+
checked={value}
|
62 |
+
label={key}
|
63 |
+
tooltip={endpoint?.tooltips?.[key]}
|
64 |
+
onChange={(e) => onParamsChange(key, e)}
|
65 |
+
/>
|
66 |
+
</div>
|
67 |
+
) : (
|
68 |
+
<TextInput
|
69 |
+
value={value as string}
|
70 |
label={key}
|
71 |
+
tooltip={endpoint?.tooltips?.[key]}
|
72 |
+
placeholder="value"
|
73 |
onChange={(e) => onParamsChange(key, e)}
|
74 |
/>
|
75 |
+
)}
|
76 |
+
</div>
|
77 |
+
))}
|
78 |
+
</div>
|
79 |
+
)}
|
80 |
+
{tab === "body" && endpoint?.body?.length && (
|
81 |
+
<div className="mt-6 grid grid-cols-2 gap-6 w-full">
|
82 |
+
<p className="text-slate-200 uppercase text-xs font-semibold col-span-2">
|
83 |
+
Body
|
84 |
+
</p>
|
85 |
+
{endpoint?.body?.length &&
|
86 |
+
endpoint?.body.map((b, key) => (
|
87 |
+
<div
|
88 |
+
key={key}
|
89 |
+
className="flex items-center justify-between gap-2"
|
90 |
+
>
|
91 |
+
{typeof b.defaultValue === "boolean" ? (
|
92 |
+
<div>
|
93 |
+
<Toggle
|
94 |
+
checked={b.defaultValue}
|
95 |
+
label={b.key}
|
96 |
+
onChange={(e) =>
|
97 |
+
setBodyForm({ ...bodyForm, [b.key]: e })
|
98 |
+
}
|
99 |
+
/>
|
100 |
+
</div>
|
101 |
+
) : (
|
102 |
+
<TextInput
|
103 |
+
value={
|
104 |
+
(formattedBody?.[
|
105 |
+
b.key as keyof typeof formattedBody
|
106 |
+
] as string) ?? (b.defaultValue as string)
|
107 |
+
}
|
108 |
label={b.key}
|
109 |
+
placeholder="value"
|
110 |
onChange={(e) => setBodyForm({ ...bodyForm, [b.key]: e })}
|
111 |
/>
|
112 |
+
)}
|
113 |
+
</div>
|
114 |
+
))}
|
115 |
+
</div>
|
116 |
+
)}
|
117 |
+
{tab === "headers" && (
|
118 |
+
<div className="mt-8 grid grid-cols-1 gap-6 w-full">
|
119 |
+
<p className="text-slate-200 uppercase text-xs font-semibold">
|
120 |
+
Headers
|
121 |
+
</p>
|
122 |
+
<TextInput
|
123 |
+
value={headers?.Authorization}
|
124 |
+
label="Authorization"
|
125 |
+
placeholder="Authorization"
|
126 |
+
onlyAlphaNumeric={false}
|
127 |
+
onChange={(Authorization) =>
|
128 |
+
setHeaders({ ...headers, Authorization })
|
129 |
+
}
|
130 |
+
/>
|
131 |
+
</div>
|
132 |
+
)}
|
133 |
+
{tab === "snippet" && (
|
134 |
+
<Snippet
|
135 |
+
endpoint={{ ...endpoint, path: formattedEndpoint }}
|
136 |
+
parameters={parameters}
|
137 |
+
body={formattedBody}
|
138 |
+
/>
|
139 |
+
)}
|
|
|
|
|
|
|
140 |
</div>
|
141 |
</div>
|
142 |
);
|
components/editor/main/snippet/curl.tsx
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
export const Curl = {};
|
components/editor/main/snippet/index.tsx
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ApiRoute } from "@/utils/type";
|
2 |
+
import classNames from "classnames";
|
3 |
+
import { useState } from "react";
|
4 |
+
import Highlight from "react-highlight";
|
5 |
+
import { Options } from "redaxios";
|
6 |
+
|
7 |
+
const LANGUAGES = ["curl", "javascript", "python"];
|
8 |
+
|
9 |
+
export const Snippet = ({
|
10 |
+
endpoint,
|
11 |
+
parameters,
|
12 |
+
body,
|
13 |
+
}: {
|
14 |
+
endpoint: ApiRoute;
|
15 |
+
parameters?: Record<string, any>;
|
16 |
+
body?: Options | undefined;
|
17 |
+
}) => {
|
18 |
+
const [language, setLanguage] = useState<string>(LANGUAGES[0]);
|
19 |
+
|
20 |
+
const generateRequestFromEndpoint = () => {
|
21 |
+
const { method, path } = endpoint;
|
22 |
+
|
23 |
+
const needBody = ["post", "put", "patch", "delete"].includes(
|
24 |
+
method.toLocaleLowerCase()
|
25 |
+
);
|
26 |
+
if (language === "curl") {
|
27 |
+
if (needBody && body) {
|
28 |
+
return (
|
29 |
+
`curl -X ${method.toLocaleUpperCase()} ${path} \\` +
|
30 |
+
"\n" +
|
31 |
+
` -H "Content-Type: application/json" \\` +
|
32 |
+
"\n" +
|
33 |
+
` -d '${JSON.stringify(body, null, 2)}'`
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
if (parameters) {
|
38 |
+
return (
|
39 |
+
`curl -X ${method.toLocaleUpperCase()} ${path}?` +
|
40 |
+
Object.entries(parameters)
|
41 |
+
.map(([key, value]) => `${key}=${value}`)
|
42 |
+
.join("&")
|
43 |
+
);
|
44 |
+
}
|
45 |
+
|
46 |
+
return `curl -X ${method.toLocaleUpperCase()} ${path}`;
|
47 |
+
}
|
48 |
+
|
49 |
+
if (language === "javascript") {
|
50 |
+
if (needBody && body) {
|
51 |
+
return `const response = await fetch("${path}", {
|
52 |
+
method: "${method.toLocaleUpperCase()}",
|
53 |
+
headers: {
|
54 |
+
"Content-Type": "application/json",
|
55 |
+
},
|
56 |
+
body: JSON.stringify(${JSON.stringify(body, null, 2)}),
|
57 |
+
});`;
|
58 |
+
}
|
59 |
+
if (parameters) {
|
60 |
+
return `const response = await fetch("${path}?${Object.entries(
|
61 |
+
parameters
|
62 |
+
)
|
63 |
+
.map(([key, value]) => `${key}=${value}`)
|
64 |
+
.join("&")}", {
|
65 |
+
method: "${method.toLocaleUpperCase()}",
|
66 |
+
});`;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
if (language === "python") {
|
71 |
+
if (needBody && body) {
|
72 |
+
return `import requests
|
73 |
+
response = requests.${method.toLocaleLowerCase()}("${path}", json=${JSON.stringify(
|
74 |
+
body,
|
75 |
+
null,
|
76 |
+
2
|
77 |
+
)})`;
|
78 |
+
}
|
79 |
+
if (parameters) {
|
80 |
+
return `import requests
|
81 |
+
response = requests.${method.toLocaleLowerCase()}("${path}", params={
|
82 |
+
${Object.entries(parameters)
|
83 |
+
.map(([key, value]) => `${key}: ${value}`)
|
84 |
+
.join(",\n ")}
|
85 |
+
})`;
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
return "";
|
90 |
+
};
|
91 |
+
|
92 |
+
return (
|
93 |
+
<div className="mt-8 grid grid-cols-1 gap-4 w-full">
|
94 |
+
<p className="text-slate-200 uppercase text-xs font-semibold">Snippet</p>
|
95 |
+
<div className="bg-slate-950/50 rounded-xl overflow-hidden">
|
96 |
+
<ul className="bg-slate-950 flex items-center justify-start">
|
97 |
+
{LANGUAGES.map((lang, key) => (
|
98 |
+
<li
|
99 |
+
key={key}
|
100 |
+
className={classNames(
|
101 |
+
"py-4 text-slate-300 text-xs font-semibold px-6 border-r border-slate-900 cursor-pointer hover:bg-slate-900/80 transition-all duration-75",
|
102 |
+
{
|
103 |
+
"bg-slate-900/50 hover:!bg-slate-900/50": lang === language,
|
104 |
+
}
|
105 |
+
)}
|
106 |
+
onClick={() => setLanguage(lang)}
|
107 |
+
>
|
108 |
+
{lang}
|
109 |
+
</li>
|
110 |
+
))}
|
111 |
+
</ul>
|
112 |
+
<main className="px-6 py-6">
|
113 |
+
<Highlight
|
114 |
+
className={`${language} text-xs font-code !bg-transparent !p-0 !whitespace-pre-wrap break-all !leading-relaxed`}
|
115 |
+
>
|
116 |
+
{generateRequestFromEndpoint()}
|
117 |
+
</Highlight>
|
118 |
+
{/* <pre className="text-slate-300 text-xs font-medium">
|
119 |
+
{generateCurlRequestFromEndpoint()}
|
120 |
+
</pre> */}
|
121 |
+
</main>
|
122 |
+
</div>
|
123 |
+
</div>
|
124 |
+
);
|
125 |
+
};
|
components/editor/main/tabs.tsx
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ApiRoute } from "@/utils/type";
|
2 |
+
import classNames from "classnames";
|
3 |
+
|
4 |
+
// const TABS = ["headers", "parameters", "body", "snippet"];
|
5 |
+
export const Tabs = ({
|
6 |
+
active,
|
7 |
+
setActive,
|
8 |
+
endpoint,
|
9 |
+
}: {
|
10 |
+
active: "headers" | "parameters" | "body" | "snippet";
|
11 |
+
setActive: (active: "headers" | "parameters" | "body" | "snippet") => void;
|
12 |
+
endpoint: ApiRoute;
|
13 |
+
}) => {
|
14 |
+
return (
|
15 |
+
<ul className="flex items-center justify-center gap-6 bg-slate-950/40">
|
16 |
+
<li
|
17 |
+
className={classNames(
|
18 |
+
"text-sm text-slate-400 hover:text-slate-400 font-semibold uppercase -tracking-wider cursor-pointer py-4 px-2 border-b-2 border-transparent",
|
19 |
+
{
|
20 |
+
"!border-slate-100 !text-slate-100": "headers" === active,
|
21 |
+
}
|
22 |
+
)}
|
23 |
+
onClick={() => setActive("headers")}
|
24 |
+
>
|
25 |
+
Headers
|
26 |
+
</li>
|
27 |
+
{endpoint?.parameters && (
|
28 |
+
<li
|
29 |
+
className={classNames(
|
30 |
+
"text-sm text-slate-400 hover:text-slate-400 font-semibold uppercase -tracking-wider cursor-pointer py-4 px-2 border-b-2 border-transparent",
|
31 |
+
{
|
32 |
+
"!border-slate-100 !text-slate-100": "parameters" === active,
|
33 |
+
}
|
34 |
+
)}
|
35 |
+
onClick={() => setActive("parameters")}
|
36 |
+
>
|
37 |
+
Parameters
|
38 |
+
</li>
|
39 |
+
)}
|
40 |
+
{endpoint?.body && (
|
41 |
+
<li
|
42 |
+
className={classNames(
|
43 |
+
"text-sm text-slate-400 hover:text-slate-400 font-semibold uppercase -tracking-wider cursor-pointer py-4 px-2 border-b-2 border-transparent",
|
44 |
+
{
|
45 |
+
"!border-slate-100 !text-slate-100": "body" === active,
|
46 |
+
}
|
47 |
+
)}
|
48 |
+
onClick={() => setActive("body")}
|
49 |
+
>
|
50 |
+
Body
|
51 |
+
</li>
|
52 |
+
)}
|
53 |
+
<li
|
54 |
+
className={classNames(
|
55 |
+
"text-sm text-slate-400 hover:text-slate-400 font-semibold uppercase -tracking-wider cursor-pointer py-4 px-2 border-b-2 border-transparent",
|
56 |
+
{
|
57 |
+
"!border-slate-100 !text-slate-100": "snippet" === active,
|
58 |
+
}
|
59 |
+
)}
|
60 |
+
onClick={() => setActive("snippet")}
|
61 |
+
>
|
62 |
+
Snippet
|
63 |
+
</li>
|
64 |
+
</ul>
|
65 |
+
);
|
66 |
+
};
|
components/input/input.tsx
CHANGED
@@ -1,20 +1,21 @@
|
|
1 |
import React, { ChangeEvent, useState } from "react";
|
2 |
import { useUpdateEffect } from "react-use";
|
|
|
3 |
|
4 |
interface Props {
|
5 |
value: string;
|
6 |
onChange: (value: string) => void;
|
7 |
placeholder?: string;
|
|
|
8 |
label?: string;
|
9 |
onlyAlphaNumeric?: boolean;
|
10 |
-
subLabel?: string;
|
11 |
}
|
12 |
|
13 |
export const TextInput: React.FC<Props> = ({
|
14 |
value: initialValue,
|
15 |
onChange,
|
16 |
placeholder,
|
17 |
-
|
18 |
onlyAlphaNumeric = true,
|
19 |
label,
|
20 |
}) => {
|
@@ -33,10 +34,19 @@ export const TextInput: React.FC<Props> = ({
|
|
33 |
|
34 |
return (
|
35 |
<div className="w-full relative grid grid-cols-1 gap-2.5">
|
36 |
-
<
|
37 |
-
{
|
38 |
-
|
39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
<input
|
41 |
type="text"
|
42 |
value={value}
|
|
|
1 |
import React, { ChangeEvent, useState } from "react";
|
2 |
import { useUpdateEffect } from "react-use";
|
3 |
+
import { HiInformationCircle } from "react-icons/hi";
|
4 |
|
5 |
interface Props {
|
6 |
value: string;
|
7 |
onChange: (value: string) => void;
|
8 |
placeholder?: string;
|
9 |
+
tooltip?: string;
|
10 |
label?: string;
|
11 |
onlyAlphaNumeric?: boolean;
|
|
|
12 |
}
|
13 |
|
14 |
export const TextInput: React.FC<Props> = ({
|
15 |
value: initialValue,
|
16 |
onChange,
|
17 |
placeholder,
|
18 |
+
tooltip,
|
19 |
onlyAlphaNumeric = true,
|
20 |
label,
|
21 |
}) => {
|
|
|
34 |
|
35 |
return (
|
36 |
<div className="w-full relative grid grid-cols-1 gap-2.5">
|
37 |
+
<div className="flex items-center justify-start gap-2 relative">
|
38 |
+
{tooltip && (
|
39 |
+
<div className="group cursor-pointer">
|
40 |
+
<HiInformationCircle className="text-slate-500 group-hover:text-slate-300 text-xl" />
|
41 |
+
<div className="bg-slate-950/90 z-10 rounded-xl p-3 text-white absolute text-xs left-0 bottom-0 translate-y-[calc(100%+8px)] opacity-0 transition-all duration-200 group-hover:opacity-100 pointer-events-none group-hover:pointer-events-auto">
|
42 |
+
{tooltip}
|
43 |
+
</div>
|
44 |
+
</div>
|
45 |
+
)}
|
46 |
+
<label className="text-slate-400 text-sm font-medium capitalize">
|
47 |
+
{label}:
|
48 |
+
</label>
|
49 |
+
</div>
|
50 |
<input
|
51 |
type="text"
|
52 |
value={value}
|
components/input/toggle.tsx
CHANGED
@@ -1,23 +1,40 @@
|
|
1 |
import classNames from "classnames";
|
2 |
import { useState } from "react";
|
|
|
3 |
import { useUpdateEffect } from "react-use";
|
4 |
|
5 |
interface Props {
|
6 |
label: string;
|
7 |
checked: boolean;
|
|
|
8 |
onChange: (checked: boolean) => void;
|
9 |
}
|
10 |
|
11 |
-
export const Toggle: React.FC<Props> = ({
|
|
|
|
|
|
|
|
|
|
|
12 |
const [checkedState, setCheckedState] = useState(checked);
|
13 |
|
14 |
useUpdateEffect(() => onChange(checkedState), [checkedState]);
|
15 |
|
16 |
return (
|
17 |
-
<div className="w-full flex items-center justify-start gap-2.5">
|
18 |
-
<
|
19 |
-
{
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
<div
|
22 |
className={classNames(
|
23 |
"w-[52px] h-[24px] rounded-full p-[4px] relative cursor-pointer",
|
|
|
1 |
import classNames from "classnames";
|
2 |
import { useState } from "react";
|
3 |
+
import { HiInformationCircle } from "react-icons/hi";
|
4 |
import { useUpdateEffect } from "react-use";
|
5 |
|
6 |
interface Props {
|
7 |
label: string;
|
8 |
checked: boolean;
|
9 |
+
tooltip?: string;
|
10 |
onChange: (checked: boolean) => void;
|
11 |
}
|
12 |
|
13 |
+
export const Toggle: React.FC<Props> = ({
|
14 |
+
label,
|
15 |
+
onChange,
|
16 |
+
checked,
|
17 |
+
tooltip,
|
18 |
+
}) => {
|
19 |
const [checkedState, setCheckedState] = useState(checked);
|
20 |
|
21 |
useUpdateEffect(() => onChange(checkedState), [checkedState]);
|
22 |
|
23 |
return (
|
24 |
+
<div className="w-full flex items-center justify-start gap-2.5 relative">
|
25 |
+
<div className="flex items-center justify-start gap-2">
|
26 |
+
{tooltip && (
|
27 |
+
<div className="group cursor-pointer">
|
28 |
+
<HiInformationCircle className="text-slate-500 group-hover:text-slate-300 text-xl" />
|
29 |
+
<div className="bg-slate-950/90 z-10 min-w-[200px] rounded-xl p-3 text-white absolute text-xs left-0 bottom-0 translate-y-[calc(100%+8px)] opacity-0 transition-all duration-200 group-hover:opacity-100 pointer-events-none group-hover:pointer-events-auto">
|
30 |
+
{tooltip}
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
)}
|
34 |
+
<label className="text-slate-400 text-sm font-medium capitalize">
|
35 |
+
{label}:
|
36 |
+
</label>
|
37 |
+
</div>
|
38 |
<div
|
39 |
className={classNames(
|
40 |
"w-[52px] h-[24px] rounded-full p-[4px] relative cursor-pointer",
|
utils/datas/api_collections.ts
CHANGED
@@ -15,6 +15,12 @@ export const API_COLLECTIONS: Array<ApiCollection> = [{
|
|
15 |
limit: 5,
|
16 |
full: true,
|
17 |
config: true
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
}
|
19 |
}, {
|
20 |
method: 'GET',
|
|
|
15 |
limit: 5,
|
16 |
full: true,
|
17 |
config: true
|
18 |
+
},
|
19 |
+
tooltips: {
|
20 |
+
search: "Filter based on substrings for repos and their usernames, such as resnet or microsoft",
|
21 |
+
full: "Whether to fetch most model data, such as all tags, the files, etc.",
|
22 |
+
config: "Whether to also fetch the repo config.",
|
23 |
+
filter: "Filter based on tags, such as text-classification or spacy."
|
24 |
}
|
25 |
}, {
|
26 |
method: 'GET',
|
utils/type.ts
CHANGED
@@ -8,7 +8,8 @@ export interface ApiRoute {
|
|
8 |
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
9 |
path: string,
|
10 |
parameters?: any,
|
11 |
-
body?: Array<Body
|
|
|
12 |
}
|
13 |
|
14 |
export interface Body {
|
|
|
8 |
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
9 |
path: string,
|
10 |
parameters?: any,
|
11 |
+
body?: Array<Body>,
|
12 |
+
tooltips?: Record<string, string>
|
13 |
}
|
14 |
|
15 |
export interface Body {
|