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";
5
6// MODEL
7
8export const Model = S.Struct({ count: S.Number });
9export type Model = typeof Model.Type;
10
11// MESSAGE
12
13export const Message = defineMessageUnion({
14 ClickedDecrement: {},
15 ClickedIncrement: {},
16 ClickedReset: {},
17});
18export const { ClickedDecrement, ClickedIncrement, ClickedReset } = Message;
19export type Message = typeof Message.Type;
20
21// UPDATE
22
23export const update = (
24 model: Model,
25 message: Message,
26): Update.Return<Model, Message> =>
27 M.value(message).pipe(
28 M.withReturnType<Update.Return<Model, Message>>(),
29 M.tagsExhaustive({
30 ClickedDecrement: () => ({ model: { count: model.count - 1 } }),
31 ClickedIncrement: () => ({ model: { count: model.count + 1 } }),
32 ClickedReset: () => ({ model: { count: 0 } }),
33 }),
34 );
35
36// INIT
37
38export const init: Runtime.ApplicationInit<Model, Message> = () => ({
39 model: { count: 0 },
40});
41
42// VIEW
43
44const buttonClass =
45 "rounded-lg bg-slate-800 px-4 py-2 font-semibold text-white hover:bg-slate-700";
46
47export const view = (model: Model, h: HtmlBuilder<Message>): Document => ({
48 title: `Counter: ${model.count}`,
49 body: h.div(
50 [
51 h.Id("app"),
52 h.Class(
53 "flex min-h-screen flex-col items-center justify-center gap-6 bg-slate-100",
54 ),
55 ],
56 [
57 h.p(
58 [h.Id("count"), h.Class("text-3xl font-bold text-slate-900")],
59 [model.count.toString()],
60 ),
61 h.div(
62 [h.Class("flex gap-3")],
63 [
64 h.button(
65 [h.OnClick(ClickedDecrement()), h.Class(buttonClass)],
66 ["-"],
67 ),
68 h.button(
69 [h.OnClick(ClickedReset()), h.Class(buttonClass)],
70 ["Reset"],
71 ),
72 h.button(
73 [h.OnClick(ClickedIncrement()), h.Class(buttonClass)],
74 ["+"],
75 ),
76 ],
77 ),
78 ],
79 ),
80});