zudoku 0.1.1-dev.29 → 0.1.1-dev.30
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/dist/lib/components/Dialog.js +1 -1
- package/dist/lib/components/Dialog.js.map +1 -1
- package/dist/lib/components/SyntaxHighlight.d.ts +1 -1
- package/dist/lib/components/SyntaxHighlight.js +3 -0
- package/dist/lib/components/SyntaxHighlight.js.map +1 -1
- package/dist/lib/plugins/openapi/ColorizedParam.d.ts +1 -2
- package/dist/lib/plugins/openapi/ColorizedParam.js +1 -1
- package/dist/lib/plugins/openapi/ColorizedParam.js.map +1 -1
- package/dist/lib/plugins/openapi/MakeRequest.js +1 -1
- package/dist/lib/plugins/openapi/MakeRequest.js.map +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js +1 -1
- package/dist/lib/plugins/openapi/ParameterListItem.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/Editor.d.ts +1 -0
- package/dist/lib/plugins/openapi/playground/Editor.js +5 -0
- package/dist/lib/plugins/openapi/playground/Editor.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/Headers.d.ts +7 -0
- package/dist/lib/plugins/openapi/playground/Headers.js +26 -0
- package/dist/lib/plugins/openapi/playground/Headers.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/InlineInput.d.ts +3 -0
- package/dist/lib/plugins/openapi/playground/InlineInput.js +3 -0
- package/dist/lib/plugins/openapi/playground/InlineInput.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/Playground.d.ts +18 -1
- package/dist/lib/plugins/openapi/playground/Playground.js +60 -71
- package/dist/lib/plugins/openapi/playground/Playground.js.map +1 -1
- package/dist/lib/plugins/openapi/playground/QueryParams.d.ts +7 -0
- package/dist/lib/plugins/openapi/playground/QueryParams.js +30 -0
- package/dist/lib/plugins/openapi/playground/QueryParams.js.map +1 -0
- package/dist/lib/plugins/openapi/playground/UrlParts.d.ts +6 -0
- package/dist/lib/plugins/openapi/playground/UrlParts.js +21 -0
- package/dist/lib/plugins/openapi/playground/UrlParts.js.map +1 -0
- package/dist/lib/util/createVariantComponent.d.ts +3 -10
- package/dist/lib/util/createVariantComponent.js +3 -2
- package/dist/lib/util/createVariantComponent.js.map +1 -1
- package/lib/{DevPortal-ChiyAyW7.js → DevPortal-COMmOqxP.js} +4082 -4379
- package/lib/assets/{worker-CnXQsqxH.js → worker-W78u54MC.js} +2840 -2878
- package/lib/zudoku.components.js +1 -1
- package/lib/zudoku.openapi-worker.js +5 -5
- package/lib/zudoku.plugins.js +8197 -6903
- package/package.json +10 -1
- package/src/lib/components/Dialog.tsx +32 -32
- package/src/lib/components/SyntaxHighlight.tsx +4 -0
- package/src/lib/plugins/openapi/ColorizedParam.tsx +0 -2
- package/src/lib/plugins/openapi/MakeRequest.tsx +1 -1
- package/src/lib/plugins/openapi/ParameterListItem.tsx +4 -1
- package/src/lib/plugins/openapi/playground/Editor.tsx +6 -0
- package/src/lib/plugins/openapi/playground/Headers.tsx +66 -0
- package/src/lib/plugins/openapi/playground/InlineInput.tsx +6 -0
- package/src/lib/plugins/openapi/playground/Playground.tsx +191 -226
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +79 -0
- package/src/lib/plugins/openapi/playground/UrlParts.tsx +67 -0
- package/src/lib/util/createVariantComponent.tsx +7 -5
|
@@ -9,45 +9,51 @@ import {
|
|
|
9
9
|
import { Card, CardContent } from "../../../ui/Card.js";
|
|
10
10
|
import { Button } from "../../../ui/Button.js";
|
|
11
11
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../../ui/Tabs.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
Fragment,
|
|
15
|
-
InputHTMLAttributes,
|
|
16
|
-
useEffect,
|
|
17
|
-
useRef,
|
|
18
|
-
useState,
|
|
19
|
-
} from "react";
|
|
20
|
-
import { TrashIcon } from "lucide-react";
|
|
21
|
-
import createVariantComponent from "../../../util/createVariantComponent.js";
|
|
12
|
+
import { Fragment } from "react";
|
|
22
13
|
import { SyntaxHighlight } from "../../../components/SyntaxHighlight.js";
|
|
23
|
-
import {
|
|
24
|
-
ColorizedParam,
|
|
25
|
-
DATA_ATTR,
|
|
26
|
-
usePastellizedColor,
|
|
27
|
-
} from "../ColorizedParam.js";
|
|
28
|
-
import { cn } from "../../../util/cn.js";
|
|
14
|
+
import { ColorizedParam } from "../ColorizedParam.js";
|
|
29
15
|
import { useMutation } from "@tanstack/react-query";
|
|
16
|
+
import { useForm } from "react-hook-form";
|
|
17
|
+
import { Headers } from "./Headers.js";
|
|
18
|
+
import { QueryParams } from "./QueryParams.js";
|
|
19
|
+
import { UrlParts } from "./UrlParts.js";
|
|
20
|
+
import { LoaderCircle } from "lucide-react";
|
|
30
21
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
22
|
+
function mimeTypeToLanguage(mimeType: string) {
|
|
23
|
+
const mimeTypeMapping = {
|
|
24
|
+
"application/json": "json",
|
|
25
|
+
"text/json": "json",
|
|
26
|
+
"text/html": "html",
|
|
27
|
+
"text/css": "css",
|
|
28
|
+
"text/javascript": "javascript",
|
|
29
|
+
"application/xml": "xml",
|
|
30
|
+
"application/xhtml+xml": "xhtml",
|
|
31
|
+
"text/plain": "plain",
|
|
32
|
+
} as const;
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
34
|
+
return Object.entries(mimeTypeMapping).find(([mime]) =>
|
|
35
|
+
mimeType.includes(mime),
|
|
36
|
+
)?.[0][1];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type Header = {
|
|
40
|
+
name: string;
|
|
41
|
+
value: string;
|
|
42
|
+
};
|
|
43
|
+
export type QueryParam = {
|
|
44
|
+
name: string;
|
|
45
|
+
value: string;
|
|
46
|
+
};
|
|
47
|
+
export type UrlPart = {
|
|
48
|
+
name: string;
|
|
49
|
+
value: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type PlaygroundForm = {
|
|
53
|
+
body: string;
|
|
54
|
+
queryParams: QueryParam[];
|
|
55
|
+
urlParts: UrlPart[];
|
|
56
|
+
headers: Header[];
|
|
51
57
|
};
|
|
52
58
|
|
|
53
59
|
const Playground = ({
|
|
@@ -61,41 +67,38 @@ const Playground = ({
|
|
|
61
67
|
method: string;
|
|
62
68
|
defaultHeaders?: Header[];
|
|
63
69
|
}) => {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const { register, control, handleSubmit, watch } = useForm<PlaygroundForm>({
|
|
71
|
+
defaultValues: {
|
|
72
|
+
body: "",
|
|
73
|
+
queryParams: [{ name: "", value: "" }],
|
|
74
|
+
headers: defaultHeaders ?? [{ name: "", value: "" }],
|
|
75
|
+
urlParts: url
|
|
76
|
+
.split("/")
|
|
77
|
+
.filter((part) => part.startsWith("{") && part.endsWith("}"))
|
|
78
|
+
.map((part) => ({ name: part.slice(1, -1), value: "" })),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
const lastHeader = headers.at(-1);
|
|
76
|
-
if (lastHeader?.value !== "" || lastHeader?.name !== "") {
|
|
77
|
-
setHeaders([...headers, { name: "", value: "" }]);
|
|
78
|
-
}
|
|
79
|
-
}, [headers]);
|
|
82
|
+
const formState = watch();
|
|
80
83
|
|
|
81
84
|
const x = useMutation({
|
|
82
|
-
mutationFn: async () => {
|
|
85
|
+
mutationFn: async (data: PlaygroundForm) => {
|
|
83
86
|
const fullUrl =
|
|
84
87
|
host +
|
|
85
88
|
url
|
|
86
89
|
.split("/")
|
|
87
|
-
.map((v) =>
|
|
90
|
+
.map((v) =>
|
|
91
|
+
v.startsWith("{") && v.endsWith("}")
|
|
92
|
+
? data.urlParts.find((part) => part.name === v.slice(1, -1))
|
|
93
|
+
?.value ?? v
|
|
94
|
+
: v,
|
|
95
|
+
)
|
|
88
96
|
.join("/");
|
|
89
|
-
|
|
90
|
-
fullUrl,
|
|
91
|
-
Object.fromEntries(
|
|
92
|
-
headers.map((header) => [header.name, header.value]),
|
|
93
|
-
),
|
|
94
|
-
);
|
|
97
|
+
|
|
95
98
|
const response = await fetch(fullUrl, {
|
|
96
|
-
|
|
99
|
+
method: method.toUpperCase(),
|
|
97
100
|
headers: Object.fromEntries(
|
|
98
|
-
|
|
101
|
+
data.urlParts
|
|
99
102
|
.filter((h) => h.name)
|
|
100
103
|
.map((header) => [header.name, header.value]),
|
|
101
104
|
),
|
|
@@ -103,6 +106,7 @@ const Playground = ({
|
|
|
103
106
|
|
|
104
107
|
return {
|
|
105
108
|
status: response.status,
|
|
109
|
+
headers: response.headers,
|
|
106
110
|
body: await response.text(),
|
|
107
111
|
};
|
|
108
112
|
},
|
|
@@ -119,188 +123,149 @@ const Playground = ({
|
|
|
119
123
|
}}
|
|
120
124
|
slug={part.slice(1, -1)}
|
|
121
125
|
>
|
|
122
|
-
{
|
|
126
|
+
{
|
|
127
|
+
formState.urlParts.find((p) => {
|
|
128
|
+
console.log(
|
|
129
|
+
p.name,
|
|
130
|
+
part.slice(1, -1),
|
|
131
|
+
p.name === part.slice(1, -1),
|
|
132
|
+
p.value,
|
|
133
|
+
);
|
|
134
|
+
return p.name === part.slice(1, -1);
|
|
135
|
+
})?.value
|
|
136
|
+
}
|
|
123
137
|
</ColorizedParam>
|
|
124
138
|
) : (
|
|
125
139
|
part
|
|
126
140
|
)}
|
|
127
|
-
/
|
|
141
|
+
{"/"}
|
|
128
142
|
<wbr />
|
|
129
143
|
</Fragment>
|
|
130
144
|
));
|
|
131
145
|
|
|
146
|
+
const lang = mimeTypeToLanguage(x.data?.headers.get("Content-Type") ?? "");
|
|
147
|
+
|
|
132
148
|
return (
|
|
133
149
|
<Dialog>
|
|
134
150
|
<DialogTrigger>Open</DialogTrigger>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
|
|
152
|
+
<DialogContent className="max-w-screen-xl w-full h-5/6">
|
|
153
|
+
<form
|
|
154
|
+
onSubmit={handleSubmit((data) => {
|
|
155
|
+
x.mutateAsync(data);
|
|
156
|
+
})}
|
|
157
|
+
>
|
|
158
|
+
<DialogHeader>
|
|
159
|
+
<DialogTitle className="mb-4">API Playground</DialogTitle>
|
|
160
|
+
<DialogDescription>
|
|
161
|
+
<div className="grid grid-cols-2 gap-2">
|
|
162
|
+
<Card>
|
|
163
|
+
<CardContent className="border-b border-border pt-4">
|
|
164
|
+
<div className="font-bold mb-1">URL</div>
|
|
165
|
+
<div className="flex gap-2 items-center">
|
|
166
|
+
<div className="border rounded border-border flex justify-stretch w-full">
|
|
167
|
+
<div className="border-r border-border p-2 bg-muted font-mono">
|
|
168
|
+
{method.toUpperCase()}
|
|
169
|
+
</div>
|
|
170
|
+
<div className="p-2 whitespace-nowrap overflow-scroll flex-1">
|
|
171
|
+
{path}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<Button type="submit">Send</Button>
|
|
146
175
|
</div>
|
|
147
|
-
<Button onClick={() => x.mutateAsync()}>Send</Button>
|
|
148
|
-
</div>
|
|
149
|
-
</CardContent>
|
|
150
|
-
<Tabs defaultValue="headers">
|
|
151
|
-
<CardContent className="border-b border-border py-4">
|
|
152
|
-
<TabsList>
|
|
153
|
-
<TabsTrigger value="headers">
|
|
154
|
-
Headers ({headers.length})
|
|
155
|
-
</TabsTrigger>
|
|
156
|
-
<TabsTrigger value="parameters">Parameters</TabsTrigger>
|
|
157
|
-
<TabsTrigger value="auth">Auth</TabsTrigger>
|
|
158
|
-
</TabsList>
|
|
159
176
|
</CardContent>
|
|
177
|
+
<Tabs defaultValue="parameters">
|
|
178
|
+
<CardContent className="border-b border-border py-4">
|
|
179
|
+
<TabsList>
|
|
180
|
+
<TabsTrigger value="parameters">Parameters</TabsTrigger>
|
|
181
|
+
<TabsTrigger value="headers">
|
|
182
|
+
Headers ({formState.headers.length - 1})
|
|
183
|
+
</TabsTrigger>
|
|
184
|
+
<TabsTrigger
|
|
185
|
+
value="body"
|
|
186
|
+
disabled={["POST", "PUT", "PATCH", "DELETE"].includes(
|
|
187
|
+
method.toUpperCase(),
|
|
188
|
+
)}
|
|
189
|
+
>
|
|
190
|
+
Body
|
|
191
|
+
</TabsTrigger>
|
|
192
|
+
</TabsList>
|
|
193
|
+
</CardContent>
|
|
194
|
+
<CardContent className="overflow-auto h-full">
|
|
195
|
+
<TabsContent value="headers">
|
|
196
|
+
<Headers
|
|
197
|
+
control={control}
|
|
198
|
+
register={register}
|
|
199
|
+
headers={formState.headers}
|
|
200
|
+
/>
|
|
201
|
+
</TabsContent>
|
|
202
|
+
<TabsContent value="parameters">
|
|
203
|
+
<UrlParts control={control} register={register} />
|
|
204
|
+
Query Parameters
|
|
205
|
+
<QueryParams
|
|
206
|
+
control={control}
|
|
207
|
+
queryParams={formState.queryParams}
|
|
208
|
+
register={register}
|
|
209
|
+
/>
|
|
210
|
+
</TabsContent>
|
|
211
|
+
<TabsContent value="auth">
|
|
212
|
+
<CardContent>
|
|
213
|
+
{url
|
|
214
|
+
.split("/")
|
|
215
|
+
.map((part, i) =>
|
|
216
|
+
part.startsWith("{") && part.endsWith("}")
|
|
217
|
+
? formState.urlParts[i]
|
|
218
|
+
: part,
|
|
219
|
+
)
|
|
220
|
+
.join("/")}
|
|
221
|
+
</CardContent>
|
|
222
|
+
Change your password here.
|
|
223
|
+
</TabsContent>
|
|
224
|
+
<TabsContent value="body">
|
|
225
|
+
<textarea
|
|
226
|
+
{...register("body")}
|
|
227
|
+
className="border border-border w-full rounded p-2 bg-muted h-40"
|
|
228
|
+
/>
|
|
229
|
+
</TabsContent>
|
|
230
|
+
</CardContent>
|
|
231
|
+
</Tabs>
|
|
232
|
+
</Card>
|
|
233
|
+
<Card>
|
|
160
234
|
<CardContent>
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
newHeaders[i] = {
|
|
174
|
-
...headers[i],
|
|
175
|
-
name: e.target.value,
|
|
176
|
-
};
|
|
177
|
-
return newHeaders;
|
|
178
|
-
})
|
|
179
|
-
}
|
|
180
|
-
value={header.name}
|
|
181
|
-
placeholder={"Name"}
|
|
182
|
-
className="peer"
|
|
183
|
-
/>
|
|
184
|
-
<InlineInput
|
|
185
|
-
onChange={(e) =>
|
|
186
|
-
setHeaders((headers) => {
|
|
187
|
-
const newHeaders = [...headers];
|
|
188
|
-
newHeaders[i] = {
|
|
189
|
-
name: "",
|
|
190
|
-
...headers.at(i),
|
|
191
|
-
value: e.target.value,
|
|
192
|
-
};
|
|
193
|
-
return newHeaders;
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
value={header.value}
|
|
197
|
-
placeholder={"Value"}
|
|
198
|
-
className="peer"
|
|
199
|
-
/>
|
|
200
|
-
<button
|
|
201
|
-
className="hover:bg-black/5 p-1 rounded mr-2 text-muted-foreground invisible group-hover:visible peer-focus:visible"
|
|
202
|
-
onClick={() => {
|
|
203
|
-
setHeaders((headers) =>
|
|
204
|
-
[...headers].toSpliced(i, 1),
|
|
205
|
-
);
|
|
206
|
-
}}
|
|
207
|
-
>
|
|
208
|
-
<TrashIcon size={16} />
|
|
209
|
-
</button>
|
|
210
|
-
<div className="col-span-full border-b border-border"></div>
|
|
211
|
-
</div>
|
|
212
|
-
))}
|
|
213
|
-
</div>
|
|
214
|
-
</TabsContent>
|
|
215
|
-
<TabsContent value="parameters">
|
|
216
|
-
Parameters
|
|
217
|
-
<div className="grid grid-cols-[1fr_1fr_auto]">
|
|
218
|
-
{urlParts.map(({ name, value }, i) => (
|
|
219
|
-
<div
|
|
220
|
-
key={i}
|
|
221
|
-
className="grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-muted rounded overflow-hidden group"
|
|
222
|
-
>
|
|
223
|
-
{/*<input type="checkbox" />*/}
|
|
224
|
-
<InlineInput
|
|
225
|
-
onChange={(e) =>
|
|
226
|
-
setHeaders((headers) => {
|
|
227
|
-
const newHeaders = [...headers];
|
|
228
|
-
newHeaders[i] = {
|
|
229
|
-
...headers[i],
|
|
230
|
-
name: e.target.value,
|
|
231
|
-
};
|
|
232
|
-
return newHeaders;
|
|
233
|
-
})
|
|
234
|
-
}
|
|
235
|
-
value={name.slice(1, -1)}
|
|
236
|
-
disabled
|
|
237
|
-
placeholder={"Name"}
|
|
238
|
-
className="peer"
|
|
239
|
-
/>
|
|
240
|
-
<ParameterValue
|
|
241
|
-
part={name.slice(1, -1)}
|
|
242
|
-
onChange={(e) =>
|
|
243
|
-
setUrlParts((parts) => {
|
|
244
|
-
const newParts = [...parts];
|
|
245
|
-
newParts[i] = {
|
|
246
|
-
name: "",
|
|
247
|
-
...parts.at(i),
|
|
248
|
-
value: e.target.value,
|
|
249
|
-
};
|
|
250
|
-
return newParts;
|
|
251
|
-
})
|
|
252
|
-
}
|
|
253
|
-
{...{ [DATA_ATTR]: name.slice(1, -1) }}
|
|
254
|
-
value={value}
|
|
255
|
-
placeholder={"Value"}
|
|
256
|
-
className="peer"
|
|
257
|
-
/>
|
|
235
|
+
<div className="mt-2 font-mono gap-2 flex">
|
|
236
|
+
{method.toUpperCase()} {x.data?.status}{" "}
|
|
237
|
+
{url
|
|
238
|
+
.split("/")
|
|
239
|
+
.map((v) =>
|
|
240
|
+
v.startsWith("{") && v.endsWith("}")
|
|
241
|
+
? formState.urlParts.find((part) => part.name === v)
|
|
242
|
+
?.value ?? v
|
|
243
|
+
: v,
|
|
244
|
+
)
|
|
245
|
+
.join("/")}
|
|
246
|
+
</div>
|
|
258
247
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
</CardContent>
|
|
275
|
-
Change your password here.
|
|
276
|
-
</TabsContent>
|
|
248
|
+
<div className="w-full overflow-auto max-h-80 rounded-xl border border-border p-4 dark:!bg-foreground/10 dark:border-transparent">
|
|
249
|
+
{x.isPending && (
|
|
250
|
+
<>
|
|
251
|
+
<LoaderCircle size={18} className="animate-spins" /> a
|
|
252
|
+
request to see the response."
|
|
253
|
+
</>
|
|
254
|
+
)}
|
|
255
|
+
<SyntaxHighlight
|
|
256
|
+
language={lang ?? "json"}
|
|
257
|
+
noBackground
|
|
258
|
+
copyable={false}
|
|
259
|
+
className="overflow-x-auto "
|
|
260
|
+
code={x.data?.body ?? JSON.stringify("")}
|
|
261
|
+
/>
|
|
262
|
+
</div>
|
|
277
263
|
</CardContent>
|
|
278
|
-
</
|
|
279
|
-
</
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
{url
|
|
284
|
-
.split("/")
|
|
285
|
-
.map((v) =>
|
|
286
|
-
v.startsWith("{") && v.endsWith("}")
|
|
287
|
-
? urlParts.find((part) => part.name === v)?.value ?? v
|
|
288
|
-
: v,
|
|
289
|
-
)
|
|
290
|
-
.join("/")}
|
|
291
|
-
<SyntaxHighlight
|
|
292
|
-
language="json"
|
|
293
|
-
noBackground
|
|
294
|
-
copyable={false}
|
|
295
|
-
className="text-xs"
|
|
296
|
-
code={x.data?.body ?? JSON.stringify("")}
|
|
297
|
-
/>
|
|
298
|
-
</CardContent>
|
|
299
|
-
{x.data?.status}
|
|
300
|
-
</Card>
|
|
301
|
-
</div>
|
|
302
|
-
</DialogDescription>
|
|
303
|
-
</DialogHeader>
|
|
264
|
+
</Card>
|
|
265
|
+
</div>
|
|
266
|
+
</DialogDescription>
|
|
267
|
+
</DialogHeader>
|
|
268
|
+
</form>
|
|
304
269
|
</DialogContent>
|
|
305
270
|
</Dialog>
|
|
306
271
|
);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Control,
|
|
3
|
+
Controller,
|
|
4
|
+
useFieldArray,
|
|
5
|
+
UseFormRegister,
|
|
6
|
+
} from "react-hook-form";
|
|
7
|
+
import { TrashIcon } from "lucide-react";
|
|
8
|
+
import { InlineInput } from "./InlineInput.js";
|
|
9
|
+
import { PlaygroundForm, QueryParam } from "./Playground.js";
|
|
10
|
+
import { useEffect } from "react";
|
|
11
|
+
|
|
12
|
+
export const QueryParams = ({
|
|
13
|
+
control,
|
|
14
|
+
register,
|
|
15
|
+
queryParams,
|
|
16
|
+
}: {
|
|
17
|
+
register: UseFormRegister<PlaygroundForm>;
|
|
18
|
+
control: Control<PlaygroundForm>;
|
|
19
|
+
queryParams: QueryParam[];
|
|
20
|
+
}) => {
|
|
21
|
+
const { fields, append, remove } = useFieldArray<PlaygroundForm>({
|
|
22
|
+
control,
|
|
23
|
+
name: "queryParams",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const queryCount = queryParams?.length;
|
|
27
|
+
const queryLastName = queryParams.at(-1)?.name;
|
|
28
|
+
const queryLastValue = queryParams.at(-1)?.value;
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
// if (queryCount === 0) {
|
|
32
|
+
// append({ name: "", value: "" });
|
|
33
|
+
// }
|
|
34
|
+
console.log(queryLastName, queryLastValue, queryCount);
|
|
35
|
+
if (queryLastName !== "" || queryLastValue !== "") {
|
|
36
|
+
console.log("appending");
|
|
37
|
+
append({ name: "", value: "" });
|
|
38
|
+
}
|
|
39
|
+
}, [append, queryLastValue, queryLastName, queryCount]);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="grid grid-cols-[1fr_1fr_auto]">
|
|
43
|
+
{fields.map((field, i) => (
|
|
44
|
+
<div
|
|
45
|
+
key={field.id}
|
|
46
|
+
className="grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-muted rounded overflow-hidden group"
|
|
47
|
+
>
|
|
48
|
+
{field.id}
|
|
49
|
+
<Controller
|
|
50
|
+
control={control}
|
|
51
|
+
render={({ field }) => {
|
|
52
|
+
return (
|
|
53
|
+
<InlineInput {...field} placeholder="Name" className="peer" />
|
|
54
|
+
);
|
|
55
|
+
}}
|
|
56
|
+
name={`queryParams.${i}.name`}
|
|
57
|
+
/>
|
|
58
|
+
<Controller
|
|
59
|
+
control={control}
|
|
60
|
+
render={({ field }) => {
|
|
61
|
+
return (
|
|
62
|
+
<InlineInput {...field} placeholder="Value" className="peer" />
|
|
63
|
+
);
|
|
64
|
+
}}
|
|
65
|
+
name={`queryParams.${i}.value`}
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
<button
|
|
69
|
+
className="hover:bg-black/5 p-1 rounded mr-2 text-muted-foreground invisible group-hover:visible peer-focus:visible"
|
|
70
|
+
onClick={() => remove(i)}
|
|
71
|
+
>
|
|
72
|
+
<TrashIcon size={16} />
|
|
73
|
+
</button>
|
|
74
|
+
<div className="col-span-full border-b border-border"></div>
|
|
75
|
+
</div>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Control, useFieldArray, UseFormRegister } from "react-hook-form";
|
|
2
|
+
import { InlineInput } from "./InlineInput.js";
|
|
3
|
+
import { PlaygroundForm } from "./Playground.js";
|
|
4
|
+
import { DATA_ATTR, usePastellizedColor } from "../ColorizedParam.js";
|
|
5
|
+
import { forwardRef, InputHTMLAttributes } from "react";
|
|
6
|
+
import { cn } from "../../../util/cn.js";
|
|
7
|
+
|
|
8
|
+
type ParameterValueProps = {
|
|
9
|
+
part: string;
|
|
10
|
+
} & InputHTMLAttributes<HTMLInputElement>;
|
|
11
|
+
|
|
12
|
+
const ParameterValue = forwardRef<HTMLInputElement, ParameterValueProps>(
|
|
13
|
+
({ part, className, ...props }, ref) => {
|
|
14
|
+
const color = usePastellizedColor(part);
|
|
15
|
+
return (
|
|
16
|
+
<InlineInput
|
|
17
|
+
{...props}
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn(className, "opacity-80 data-[active=true]:opacity-100")}
|
|
20
|
+
style={{
|
|
21
|
+
// color: `hsl(${color})`,
|
|
22
|
+
outlineColor: `hsl(${color})`,
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const UrlParts = ({
|
|
30
|
+
control,
|
|
31
|
+
register,
|
|
32
|
+
}: {
|
|
33
|
+
register: UseFormRegister<PlaygroundForm>;
|
|
34
|
+
control: Control<PlaygroundForm>;
|
|
35
|
+
}) => {
|
|
36
|
+
const { fields } = useFieldArray<PlaygroundForm>({
|
|
37
|
+
control,
|
|
38
|
+
name: "urlParts",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="grid grid-cols-[1fr_1fr_auto]">
|
|
43
|
+
{fields.map((part, i) => (
|
|
44
|
+
<div
|
|
45
|
+
key={part.id}
|
|
46
|
+
className="grid-cols-subgrid col-span-full grid items-center gap-x-2 has-[:focus]:bg-muted hover:bg-muted rounded overflow-hidden group"
|
|
47
|
+
>
|
|
48
|
+
<InlineInput
|
|
49
|
+
{...register(`urlParts.${i}.name`)}
|
|
50
|
+
disabled
|
|
51
|
+
placeholder="Name"
|
|
52
|
+
className="peer"
|
|
53
|
+
/>
|
|
54
|
+
<ParameterValue
|
|
55
|
+
{...register(`urlParts.${i}.value`)}
|
|
56
|
+
part={part.name}
|
|
57
|
+
{...{ [DATA_ATTR]: part.name }}
|
|
58
|
+
placeholder="Value"
|
|
59
|
+
className="peer"
|
|
60
|
+
/>
|
|
61
|
+
|
|
62
|
+
<div className="col-span-full border-b border-border"></div>
|
|
63
|
+
</div>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
@@ -12,15 +12,17 @@ const createVariantComponent = <
|
|
|
12
12
|
cvx: ClassValue | C,
|
|
13
13
|
// variantProps: Array<keyof VariantProps<C>> = [],
|
|
14
14
|
) => {
|
|
15
|
-
const MyVariant =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const MyVariant = React.forwardRef<
|
|
16
|
+
HTMLElement,
|
|
17
|
+
JSX.IntrinsicElements[E] & { className?: ClassValue }
|
|
18
|
+
>(({ className, ...props }, ref) =>
|
|
19
19
|
React.createElement(tag, {
|
|
20
20
|
...props,
|
|
21
|
+
ref,
|
|
21
22
|
className:
|
|
22
23
|
typeof cvx === "function" ? cvx({ className }) : cn(cvx, className),
|
|
23
|
-
})
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
24
26
|
|
|
25
27
|
MyVariant.displayName = `VariantComponent(${tag})`;
|
|
26
28
|
|