Back to packages list

Vals using clsx

Description from the NPM package:
A tiny (239B) utility for constructing className strings conditionally.
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
/** @jsxImportSource npm:preact */
import { RenderFunc } from "https://esm.town/v/pomdtr/invoice_schema";
import clsx from "npm:clsx";
import { render as preactRender } from "npm:preact-render-to-string";
export const render: RenderFunc = ({ title, table, to, from, details }) => {
return preactRender(
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<link rel="icon" href="https://fav.farm/📃" />
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-white">
<header>
<section>
<div class="px-8 py-24 flex flex-col mx-auto justify-center sm:px-12 lg:px-24 max-w-6xl">
<div class="flex justify-between items-start">
<div class="text-left text-sm space-y-1 text-gray-500">
{from.map((value) => <p>{value}</p>)}
</div>
<div class="text-right text-sm space-y-1 text-gray-500">
{to.map((value) => <p>{value}</p>)}
</div>
</div>
<div class="sm:flex sm:items-center mt-12">
<div class="sm:flex-auto">
<h1 class="text-xl">{title}</h1>
</div>
</div>
<div class="-mx-4 mt-24 flow-root sm:mx-0">
<table class="min-w-full">
<thead class="border-b border-gray-300 text-gray-900">
<tr>
{table.columns.map((column, idx) => (
<th
scope="col"
class={`px-3 py-3.5 text-left ${
clsx({
"text-left": idx == 0,
"text-right": idx > 0,
})
}`}
>
{column}
</th>
))}
</tr>
</thead>
<tbody>
{table.rows.map((row) => (
<tr class="border-b border-gray-200">
{row.map((item, idx) => (
<td
class={`hidden px-3 py-5 text-sm text-gray-500 sm:table-cell ${
clsx({
"text-left": idx == 0,
"text-right": idx > 0,
})
}`}
>
{item}
</td>
))}
</tr>
))}
</tbody>
<tfoot>
{table.summary.map(([key, value]) => (
<tr class="text-right">
<th
scope="row"
colspan={table.columns.length - 1}
class="hidden pl-4 pr-3 pt-6 sm:table-cell sm:pl-0"
>
{key}
</th>
<td class="pl-3 pr-4 pt-6 sm:pr-0 text-gray-500">{value}</td>
</tr>
))}
</tfoot>
</table>
</div>
<div class="flex justify-between mt-12 items-start">
<div class="text-left text-sm space-y-1 text-gray-500">
{details.map(([title, value]) => (
<p>
<strong>{title}:</strong> {value}
</p>
))}
</div>
</div>
</div>
</section>
</header>
</body>
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
/** @jsxImportSource https://esm.sh/preact **/
import { clsx } from "https://esm.sh/clsx";
import { parse } from "https://esm.sh/content-type";
import { Base64 } from "https://esm.sh/js-base64";
import { render } from "https://esm.sh/preact";
import { useEffect, useRef, useState } from "https://esm.sh/preact/hooks";
declare module "https://esm.sh/preact" {
namespace JSX {
interface IntrinsicElements {
"code-mirror": any;
}
}
}
export function HttpClient(props: {
bookmarks: Bookmark[];
history: HistoryItem[];
}) {
const [response, setResponse] = useState<Response>();
const [request, setRequest] = useState<Request>(
new Request(window.location.href),
);
const [history, setHistory] = useState<HistoryItem[]>(props.history);
const [selectedTab, setSelectedTab] = useState<"bookmarks" | "history">();
return (
<div class="flex flex-col h-screen">
<header class={"flex px-10 py-2 bg-white justify-between border-b"}>
<h1 class={"text-4xl font-bold"}>HTTP Client</h1>
<div class={"flex flex-row items-center gap-x-4"}>
{bookmarks.length
? (
<button
onClick={() => {
if (selectedTab === "bookmarks") {
setSelectedTab(undefined);
return;
}
setSelectedTab("bookmarks");
}}
>
<BookmarkIcon />
</button>
)
: undefined}
<button
class={"flex"}
onClick={() => {
if (selectedTab === "history") {
setSelectedTab(undefined);
return;
}
setSelectedTab("history");
}}
>
<HistoryIcon />
</button>
</div>
</header>
<div class={"flex p-3 gap-x-2 flex-grow"}>
<main class={"bg-gray flex flex-col w-9/12 mx-auto divide-y gap-y-3"}>
<div class="bg-white p-3 rounded-md border shadow">
<RequestInput
request={request}
onRequest={async (req) => {
const updated: HistoryItem[] = [
{
timestamp: Date.now(),
request: req,
},
...history,
];
setHistory(updated);
localStorage.setItem(
"history",
JSON.stringify(
await Promise.all(
updated.map(async (item) => ({
timestamp: item.timestamp,
request: await serializeRequest(item.request),
})),
),
),
);
const resp = await fetch(req);
setResponse(resp);
}}
>
</RequestInput>
</div>
{response
? (
<div class="bg-white p-3 rounded-md border shadow">
<ResponseOutput response={response}></ResponseOutput>
</div>
)
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
/** @jsxImportSource https://esm.sh/hono/jsx */
import { clsx } from "https://esm.sh/clsx";
import { useEffect, useState } from "https://esm.sh/hono/jsx/dom";
import { Island } from "https://esm.town/v/pomdtr/island";
import {
CheckCircleIcon,
ExclamationCircleIcon,
MagnifyingGlassCircleIcon,
PlayCircleIcon,
QuestionMarkCircleIcon,
StopCircleIcon,
} from "https://esm.town/v/pomdtr/test_explorer_icons";
export type Test = {
val: any;
name: string;
status?: TestStatus;
last_run_at?: string;
};
type Props = {
tests: Test[];
};
const iconSize = 20;
type TestStatus = "none" | "running" | "success" | "failure";
const StatusIcon = (props: { status: TestStatus }) => {
const style = { width: iconSize, height: iconSize };
switch (props.status) {
case "success": {
return <CheckCircleIcon style={{ ...style }} />;
}
case "failure": {
return <ExclamationCircleIcon style={style} />;
}
default: {
return <QuestionMarkCircleIcon style={style} />;
}
}
};
const RunIcon = (props: any & { status: TestStatus }) => {
const { status, ...iconProps } = props;
const style = { width: iconSize, height: iconSize };
switch (props.status) {
case "running": {
return <StopCircleIcon style={style} {...iconProps} />;
}
default: {
return <PlayCircleIcon style={style} {...iconProps} />;
}
}
};
function TestItem({ test, run }: { test: Test; run: () => void }) {
return (
<div>
<span
class={clsx({
"pico-color-red-500": test.status == "failure",
"pico-color-green-500": test.status == "success",
})}
>
<StatusIcon status={test.status} /> {test.name}
</span>{" "}
<span data-tooltip="Run Test">
<RunIcon status={test.status} onClick={run} />
</span>
</div>
);
}
function groupBySlug(tests: Test[]): Record<string, Test[]> {
const groups = {};
for (const test of Object.values(tests)) {
const slug = `${test.val.author.username}/${test.val.name}`;
if (!groups[slug]) {
groups[slug] = [test];
} else {
groups[slug].push(test);
}
}
return groups;
}
function useTests(initialTests: Test[]) {
const id = (test: Test) => `${test.val.name}/${test.name}`;
const [tests, setTests] = useState(Object.fromEntries(initialTests.map((test) => {
return [id(test), test];
})));
const groups = groupBySlug(tests);
return {
groups,
1
Next