1import { Match as M, Schema as S } from "effect";
2import type { Runtime, Update } from "foldkit";
3import type { Document, HtmlBuilder } from "foldkit/html";
4import { defineMessageUnion } from "foldkit/message";
5import { card } from "./components/Card.ts";
6
7export const Model = S.Struct({ count: S.Number });
8export type Model = typeof Model.Type;
9
10export const Message = defineMessageUnion({
11 ClickedDecrement: {},
12 ClickedIncrement: {},
13 ClickedReset: {},
14});
15export const { ClickedDecrement, ClickedIncrement, ClickedReset } = Message;
16export type Message = typeof Message.Type;
17
18export const update = (
19 model: Model,
20 message: Message,
21): Update.Return<Model, Message> =>
22 M.value(message).pipe(
23 M.withReturnType<Update.Return<Model, Message>>(),
24 M.tagsExhaustive({
25 ClickedDecrement: () => ({ model: { count: model.count - 1 } }),
26 ClickedIncrement: () => ({ model: { count: model.count + 1 } }),
27 ClickedReset: () => ({ model: { count: 0 } }),
28 }),
29 );
30
31export const init: Runtime.ApplicationInit<Model, Message> = () => ({
32 model: { count: 0 },
33});
34
35export const view = (model: Model, h: HtmlBuilder<Message>): Document => ({
36 title: "Foldkit on Prisma",
37 body: h.div(
38 [h.Id("app")],
39 [
40 h.h1([h.Class("text-3xl font-bold")], ["Foldkit on Prisma"]),
41 card(h, {
42 title: "A model/update/view counter",
43 body: "The Foldkit runtime renders each state transition in the browser.",
44 }),
45 h.p([h.Id("count")], [String(model.count)]),
46 h.button([h.OnClick(ClickedDecrement())], ["-"]),
47 h.button([h.OnClick(ClickedReset())], ["Reset"]),
48 h.button([h.OnClick(ClickedIncrement())], ["+"]),
49 h.a([h.Href("/example.json")], ["Static JSON asset"]),
50 ],
51 ),
52});