Back to packages list

Vals using hono/jsx/dom

Preview and edit blobs

Usage:

Create valimport blobEditor from "https://esm.town/v/pomdtr/blob_editor" export default blobEditor("article.md")

You can easily protect your val behind @pomdtr/password_auth or @pomdtr/email_auth

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 { useEffect, useRef } from "https://esm.sh/hono/jsx/dom";
export function BlobEditor(props: { blob: { language?: string; text: string } }) {
const ref = useRef();
const saveBlob = async () => {
const resp = await fetch("/edit", {
method: "POST",
body: JSON.stringify({
text: ref.current!.code,
}),
});
if (!resp.ok) {
alert(await resp.text());
return;
}
window.location.replace("/");
};
return (
<article style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
<code-mirror
style={{
flexGrow: 1,
backgroundColor: "#F9FAFB",
color: "#373C44",
}}
class="overflow-auto"
ref={ref}
code={props.blob.text}
language={props.blob.language}
>
</code-mirror>
<footer>
<div class="grid">
<button onClick={saveBlob}>Save</button>
</div>
</footer>
</article>
);
}
export function blobEditor(key: string, options?: { title?: string }) {
return async (req: Request) => {
const { html } = await import("https://esm.town/v/pomdtr/gfm");
const { blob } = await import("https://esm.town/v/std/blob?v=11");
const { Hono } = await import("npm:hono");
const { Island } = await import("https://esm.town/v/pomdtr/hono_island");
const { HTTPException } = await import("npm:hono/http-exception");
const { jsxRenderer, useRequestContext } = await import("npm:hono/jsx-renderer");
const { extractValInfo } = await import("https://esm.town/v/pomdtr/extractValInfo");
async function readBlob(key: string) {
const resp = await blob.get(key);
if (resp.status == 200) {
return resp.text();
} else if (resp.status == 404) {
return "";
} else {
throw new Error(await resp.text());
}
}
const router = new Hono();
router.use(jsxRenderer(({ children }) => {
const c = useRequestContext();
const { pathname } = new URL(c.req.url);
return (
<html>
<head>
<link rel="icon" href="https://fav.farm/📃" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<script
type="module"
src="https://esm.town/v/pomdtr/hydrate_hono_islands"
>
</script>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.18/dist/code-mirror.js"
>
</script>
</head>
<body
style={{
height: "100vh",
}}
>
{children}
</body>
</html>
);
}));

Hono Counter Component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/** @jsxImportSource https://esm.sh/hono/jsx */
import { useState } from "https://esm.sh/hono/jsx/dom";
export default function Counter(props: { initialCount?: number }) {
const [count, setCount] = useState(props.initialCount || 0);
return (
<button
onClick={() => {
console.log("clicked!");
setCount(count + 1);
}}
>
Count: {count}
</button>
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const islands = Array.from(document.querySelectorAll("[data-hydration-src]"));
if (islands.length > 0) {
const { render } = await import("https://esm.sh/hono/jsx/dom");
const { jsx: _jsx } = await import("https://esm.sh/hono/jsx/dom/jsx-runtime");
for (const island of islands) {
let src = island.getAttribute("data-hydration-src");
if (!src.startsWith("https://")) {
src = `https://esm.town/v/${src}`;
}
let name = island.getAttribute("data-hydration-name");
if (!name) {
name = "default";
}
const mod = await import(src);
const Component = mod[name];
if (!Component) {
throw new Error(`Component ${name} is not exported`);
}
const props = JSON.parse(island.getAttribute("data-hydration-props"));
render(_jsx(Component, props), island);
}
}
1
Next