import { readFile } from "node:fs/promises"; import { join } from "node:path"; import { Hono } from "hono"; import { serve } from "bun"; import { KanbanBoard } from "./adapter/kanban"; import { DragGroup } from "./adapter/drag-group"; import { SortableList } from "./adapter/sortable-list"; import { renderHTML } from "./adapter/render"; import type { DatastarEventBinding } from "./adapter/event-binding"; import { kanbanContract } from "../../contracts/kanban"; import { sortableListContract } from "../../contracts/sortable-list"; import { dragGroupContract } from "../../contracts/drag-group"; import fixture from "./fixture.json"; const app = new Hono(); const root = import.meta.dir; const datastarPath = join(root, "../../public/js/datastar-rocket.js"); const state = structuredClone(fixture); const kanbanMove: DatastarEventBinding = { event: kanbanContract.events.move, attrs: { "data-on:rocket-kanban-move": "$cardId = evt.detail?.['cardId'] ?? null; $col = evt.detail?.['col'] ?? null; $before = evt.detail?.['before'] ?? null; @post('/move')", }, }; const listMove: DatastarEventBinding = { event: sortableListContract.events.move, attrs: { "data-on:rocket-sortable-move": "$itemId = evt.detail?.['itemId'] ?? null; $before = evt.detail?.['before'] ?? null; @post('/list-move')", }, }; const groupMove: DatastarEventBinding = { event: dragGroupContract.events.move, attrs: { "data-on:rocket-drag-group-move": "$itemId = evt.detail?.['itemId'] ?? null; $fromList = evt.detail?.['fromList'] ?? null; $toList = evt.detail?.['toList'] ?? null; $before = evt.detail?.['before'] ?? null; @post('/group-move')", }, }; function Demo() { return (

Kanban

Sortable list

Drag group

); } function patch(): Response { const lines = ["event: datastar-patch-elements", "data: selector #rocket-demo", "data: mode outer"]; for (const line of renderHTML().split("\n")) lines.push(`data: elements ${line}`); return new Response([...lines, "", ""].join("\n"), { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-store" }, }); } async function signals(request: Request): Promise> { const payload = (await request.json()) as { signals?: Record }; return payload.signals ?? {}; } function relocate(source: T[], destination: T[], id: string, before: string): void { const item = source.find((candidate) => candidate.id === id); if (!item || before === id || (before && !destination.some((candidate) => candidate.id === before))) return; source.splice(source.indexOf(item), 1); const index = before ? destination.findIndex((candidate) => candidate.id === before) : destination.length; destination.splice(index, 0, item); } app.post("/move", async (c) => { const { cardId, col, before } = await signals(c.req.raw); const source = state.columns.find((column) => column.cards.some((card) => card.id === cardId)); const destination = state.columns.find((column) => column.id === col); if (source && destination && typeof cardId === "string" && typeof before === "string") relocate(source.cards, destination.cards, cardId, before); return patch(); }); app.post("/list-move", async (c) => { const { itemId, before } = await signals(c.req.raw); if (typeof itemId === "string" && typeof before === "string") relocate(state.list, state.list, itemId, before); return patch(); }); app.post("/group-move", async (c) => { const { itemId, fromList, toList, before } = await signals(c.req.raw); const source = state.groups.find((group) => group.id === fromList); const destination = state.groups.find((group) => group.id === toList); if (source && destination && typeof itemId === "string" && typeof before === "string") relocate(source.items, destination.items, itemId, before); return patch(); }); app.get("/", (c) => c.html( Rocket kit spike , ), ); async function asset(path: string, contentType: string): Promise { const file = Bun.file(join(root, "../../", path)); return new Response(file, { headers: { "content-type": contentType } }); } app.get("/rocket-kit.js", () => asset("dist/rocket-kit.js", "text/javascript; charset=utf-8")); app.get( "/demo.css", async () => new Response(await readFile(join(root, "demo.css")), { headers: { "content-type": "text/css; charset=utf-8" }, }), ); app.get( "/js/datastar-rocket.js", () => new Response(Bun.file(datastarPath), { headers: { "content-type": "text/javascript; charset=utf-8" }, }), ); const port = Number(process.env.ROCKET_KIT_PORT ?? 3025); serve({ fetch: app.fetch, port }); console.log(`Rocket kit demo: http://localhost:${port}`);