added minimal astro adapter + improved the example

This commit is contained in:
dswbx
2024-11-29 21:21:59 +01:00
parent 6eb0d2242f
commit 582dbd4272
11 changed files with 301 additions and 35 deletions

View File

@@ -179,3 +179,8 @@ await tsup.build({
platform: "node",
format: ["esm", "cjs"]
});
await tsup.build({
...baseConfig("astro"),
format: ["esm", "cjs"]
});

View File

@@ -160,6 +160,11 @@
"import": "./dist/adapter/node/index.js",
"require": "./dist/adapter/node/index.cjs"
},
"./adapter/astro": {
"types": "./dist/adapter/astro/index.d.ts",
"import": "./dist/adapter/astro/index.js",
"require": "./dist/adapter/astro/index.cjs"
},
"./dist/styles.css": "./dist/ui/main.css",
"./dist/manifest.json": "./dist/static/manifest.json"
},

View File

@@ -0,0 +1,21 @@
import { Api, type ApiOptions } from "bknd";
type TAstro = {
request: {
url: string;
headers: Headers;
};
};
export type Options = {
mode?: "static" | "dynamic";
} & Omit<ApiOptions, "host"> & {
host?: string;
};
export function getApi(Astro: TAstro, options: Options = { mode: "static" }) {
return new Api({
host: new URL(Astro.request.url).origin,
headers: options.mode === "dynamic" ? Astro.request.headers : undefined
});
}

View File

@@ -0,0 +1 @@
export * from "./astro.adapter";

View File

@@ -135,10 +135,10 @@ type FormInputElement = HTMLInputElement | HTMLTextAreaElement;
function EntityFormField({ fieldApi, field, action, data, ...props }: EntityFormFieldProps) {
const handleUpdate = useEvent((e: React.ChangeEvent<FormInputElement> | any) => {
if (typeof e === "object" && "target" in e) {
console.log("handleUpdate", e.target.value);
//console.log("handleUpdate", e.target.value);
fieldApi.handleChange(e.target.value);
} else {
console.log("handleUpdate-", e);
//console.log("handleUpdate-", e);
fieldApi.handleChange(e);
}
});

BIN
bun.lockb

Binary file not shown.

View File

@@ -0,0 +1,73 @@
---
interface Props {
title: string;
body: string;
done?: boolean;
}
const { done, title, body } = Astro.props;
---
<li class="link-card" data-done={done ? 1 : undefined}>
<div class="inner">
<div class="check">
<span>{done ? "✅" : "🔘"}</span>
</div>
<h2>
{title}
</h2>
<p>
{body}
</p>
</div>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card[data-done] {
background-color: #0c3e29;
}
.link-card > .inner {
position: relative;
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
opacity: 0.8;
}
.inner .check {
position: absolute;
top: 0.75rem;
right: 0.75rem;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View File

@@ -0,0 +1,137 @@
---
import Card from "../components/Card.astro";
interface Props {
title: string;
}
const { title } = Astro.props;
const path = new URL(Astro.request.url).pathname;
const items = [
{ href: "/", text: "Home (static)" },
{ href: "/ssr", text: "SSR (with auth)" },
{ href: "/admin", text: "Admin" }
];
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<nav>
{items.map((item) => (
<a href={item.href} data-active={path === item.href}>{item.text}</a>
))}
</nav>
<main>
<div class="center">
<h1>bknd + <span class="text-gradient">Astro</span></h1>
<slot name="context" />
</div>
<slot />
</main>
</body>
</html>
<style is:global>
:root {
--accent: 136, 58, 234;
--accent-light: 224, 204, 250;
--accent-dark: 49, 10, 101;
--accent-gradient: linear-gradient(
45deg,
rgb(var(--accent)),
rgb(var(--accent-light)) 30%,
white 60%
);
}
html {
font-family: system-ui, sans-serif;
background: #13151a;
}
code {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
a, a:visited {
color: white;
font-weight: bold;
}
nav {
margin: auto;
padding: 1rem;
width: 800px;
color: white;
display: flex;
flex-direction: row;
justify-content: center;
gap: 1rem;
}
nav a {
color: white;
text-decoration: none;
background-image: none;
}
nav a[data-active],
nav a:hover {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
main {
margin: auto;
padding: 1rem;
width: 800px;
max-width: calc(100% - 2rem);
color: white;
font-size: 20px;
line-height: 1.6;
}
main .center {
text-align: center;
margin-bottom: 2rem;
p {
opacity: 50%;
font-size: 1rem;
}
}
.astro-a {
position: absolute;
top: -32px;
left: 50%;
transform: translatex(-50%);
width: 220px;
height: auto;
z-index: -1;
}
h1 {
font-size: 4rem;
font-weight: 700;
line-height: 1;
text-align: center;
margin-bottom: 0.4em;
}
.text-gradient {
background-image: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 400%;
background-position: 0%;
}
</style>

View File

@@ -1,21 +1,20 @@
---
import { Api } from "bknd";
import { Admin } from "bknd/ui";
import "bknd/dist/styles.css";
export const prerender = false;
const api = new Api({
host: new URL(Astro.request.url).origin,
headers: Astro.request.headers
});
import { getApi } from "bknd/adapter/astro";
const api = getApi(Astro, { mode: "dynamic" });
const user = api.getUser();
export const prerender = false;
---
<html>
<body>
<Admin
withProvider={{ user }}
config={{ basepath: "/admin" }}
config={{ basepath: "/admin", color_scheme: "dark" }}
client:load
/>
</body>

View File

@@ -1,16 +1,29 @@
---
import { Api } from "bknd";
const api = new Api({
host: new URL(Astro.request.url).origin
});
import { getApi } from "bknd/adapter/astro";
import Card from "../components/Card.astro";
import Layout from "../layouts/Layout.astro";
const api = getApi(Astro);
const { data } = await api.data.readMany("todos");
---
<Layout title="Welcome to Astro.">
<p slot="context">Static Rendering</p>
<ul role="list" class="link-card-grid">
{data.map((todo: any) => (
<Card
done={todo.done}
title={todo.title}
body={todo.description}
/>
))}
</ul>
</Layout>
<h1>Home (static)</h1>
<h2>Data</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
<a href="/admin">Admin</a> -
<a href="/ssr">SSR (with auth)</a> -
<a href="/">Home (static)</a>
<style>
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 2rem;
padding: 0;
}
</style>

View File

@@ -1,23 +1,35 @@
---
import { Api } from "bknd";
const api = new Api({
host: new URL(Astro.request.url).origin,
headers: Astro.request.headers
});
import { getApi } from "bknd/adapter/astro";
import Card from "../components/Card.astro";
import Layout from "../layouts/Layout.astro";
const api = getApi(Astro, { mode: "dynamic" });
const { data } = await api.data.readMany("todos");
const user = api.getUser();
console.log("user", user);
export const prerender = false;
---
<h1>SSR</h1>
<h2>Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
<Layout title="Welcome to Astro.">
<p slot="context">Server Side Rendering</p>
<ul role="list" class="link-card-grid">
{data.map((todo: any) => (
<Card
done={todo.done}
title={todo.title}
body={todo.description}
/>
))}
</ul>
<div class="center">
{user ? <p>Logged in as <b>{user?.email}</b>. <a href="/api/auth/logout">Logout</a></p> : <p>Not authenticated. <a href="/admin/auth/login">Sign in</a></p>}
</div>
</Layout>
<h2>User</h1>
<pre>{JSON.stringify(user, null, 2)}</pre>
<a href="/admin">Admin</a> -
<a href="/ssr">SSR (with auth)</a> -
<a href="/">Home (static)</a>
<style>
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 2rem;
padding: 0;
}
</style>