enzostvs HF staff commited on
Commit
ff1a29c
1 Parent(s): 53e1fd3

add snippet + rework UI/UX

Browse files
components/editor/main/endpoint.tsx CHANGED
@@ -28,17 +28,22 @@ export const Endpoint = ({
28
  };
29
 
30
  return (
31
- <div className="bg-slate-900 w-full">
32
- <div className="bg-slate-950/50 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">
36
  {path_formatted.map((p, i) => {
37
  return p.editable ? (
38
  <Parameter
39
  key={i}
40
  value={p.content}
41
- onChange={(value) => handleChange(value, p.key)}
 
 
 
 
 
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 { useMemo, useState } from "react";
2
- import classNames from "classnames";
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={() => onChange(state)}
 
 
 
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 px-4 xl:px-6 py-5 overflow-auto">
36
  {children}
37
- {parameters && (
38
- <div className="mt-6 grid grid-cols-2 gap-6 w-full">
39
- <p className="text-slate-200 uppercase text-xs font-semibold col-span-2">
40
- Optional parameters
41
- </p>
42
- {parameters &&
43
- Object.entries(parameters).map(([key, value]) => (
44
- <div
45
- key={key}
46
- className="flex items-center justify-between gap-2"
47
- >
48
- {typeof value === "boolean" ? (
49
- <div>
50
- <Toggle
51
- checked={value}
 
 
 
 
 
 
 
 
 
 
52
  label={key}
 
 
53
  onChange={(e) => onParamsChange(key, e)}
54
  />
55
- </div>
56
- ) : (
57
- <TextInput
58
- value={value as string}
59
- label={key}
60
- placeholder="value"
61
- onChange={(e) => onParamsChange(key, e)}
62
- />
63
- )}
64
- </div>
65
- ))}
66
- </div>
67
- )}
68
- {body?.length && (
69
- <div className="mt-6 grid grid-cols-2 gap-6 w-full">
70
- <p className="text-slate-200 uppercase text-xs font-semibold col-span-2">
71
- Body
72
- </p>
73
- {body?.length &&
74
- body.map((b, key) => (
75
- <div
76
- key={key}
77
- className="flex items-center justify-between gap-2"
78
- >
79
- {typeof b.defaultValue === "boolean" ? (
80
- <div>
81
- <Toggle
82
- checked={b.defaultValue}
 
 
 
 
 
83
  label={b.key}
84
- // subLabel={b.label}
85
  onChange={(e) => setBodyForm({ ...bodyForm, [b.key]: e })}
86
  />
87
- </div>
88
- ) : (
89
- <TextInput
90
- value={
91
- (formattedBody?.[
92
- b.key as keyof typeof formattedBody
93
- ] as string) ?? (b.defaultValue as string)
94
- }
95
- label={b.key}
96
- subLabel={b.label}
97
- placeholder="value"
98
- onChange={(e) => setBodyForm({ ...bodyForm, [b.key]: e })}
99
- />
100
- )}
101
- </div>
102
- ))}
103
- </div>
104
- )}
105
- <div className="mt-8 grid grid-cols-1 gap-6 w-full">
106
- <p className="text-slate-200 uppercase text-xs font-semibold">
107
- Headers
108
- </p>
109
- <TextInput
110
- value={headers?.Authorization}
111
- label="Authorization"
112
- placeholder="Authorization"
113
- onlyAlphaNumeric={false}
114
- onChange={(Authorization) =>
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
- subLabel,
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
- <label className="text-slate-400 text-sm font-medium capitalize">
37
- {label}:
38
- </label>
39
- {/* {subLabel && <p className="text-slate-600 text-xs">{subLabel}</p>} */}
 
 
 
 
 
 
 
 
 
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> = ({ label, onChange, checked }) => {
 
 
 
 
 
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
- <label className="text-slate-400 text-sm font-medium capitalize">
19
- {label}:
20
- </label>
 
 
 
 
 
 
 
 
 
 
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 {