diff --git a/h/app.go b/h/app.go index 5e583f1..83ed124 100644 --- a/h/app.go +++ b/h/app.go @@ -39,7 +39,7 @@ func (a App) start(app *fiber.App) { } func HtmlView(c *fiber.Ctx, page *Page) error { - root := page.Root + root := page.Root.Render() c.Set(fiber.HeaderContentType, fiber.MIMETextHTML) if GetApp().LiveReload && root.tag == "html" { diff --git a/h/base.go b/h/base.go index 1d2ccaf..eddefc9 100644 --- a/h/base.go +++ b/h/base.go @@ -15,23 +15,27 @@ type Partial struct { Root *Node } +func (p *Partial) Render() *Node { + return p.Root +} + func (p *Partial) ToNode() *Node { return p.Root } type Page struct { - Root *Node + Root Renderable HttpMethod string } -func NewPage(root *Node) *Page { +func NewPage(root Renderable) *Page { return &Page{ HttpMethod: http.MethodGet, Root: root, } } -func NewPageWithHttpMethod(httpMethod string, root *Node) *Page { +func NewPageWithHttpMethod(httpMethod string, root Renderable) *Page { return &Page{ HttpMethod: httpMethod, Root: root, @@ -42,16 +46,16 @@ func WrapPartial(ctx *fiber.Ctx, cb func(ctx *fiber.Ctx) *Partial) *Node { return cb(ctx).Root } -func NewPartialWithHeaders(headers *Headers, root *Node) *Partial { +func NewPartialWithHeaders(headers *Headers, root Renderable) *Partial { return &Partial{ Headers: headers, - Root: root, + Root: root.Render(), } } -func NewPartial(root *Node) *Partial { +func NewPartial(root Renderable) *Partial { return &Partial{ - Root: root, + Root: root.Render(), } } diff --git a/h/livereload.go b/h/livereload.go index 7deca76..f30375b 100644 --- a/h/livereload.go +++ b/h/livereload.go @@ -24,7 +24,7 @@ func LiveReloadHandler(c *fiber.Ctx) error { return c.SendString("") } -func LiveReload() *Node { +func LiveReload() Renderable { return Div(Get("/livereload"), Trigger("every 200ms")) } diff --git a/h/render.go b/h/render.go index ed26c89..e4aae9e 100644 --- a/h/render.go +++ b/h/render.go @@ -39,14 +39,15 @@ func (page Builder) renderNode(node *Node) { node.attributes = map[string]string{} } - flatChildren := make([]*Node, 0) + flatChildren := make([]Renderable, 0) for _, child := range node.children { + c := child.Render() flatChildren = append(flatChildren, child) - if child.tag == FlagChildrenList { - for _, gc := range child.children { + if c.tag == FlagChildrenList { + for _, gc := range c.children { flatChildren = append(flatChildren, gc) } - child.tag = FlagSkip + c.tag = FlagSkip } } @@ -56,30 +57,32 @@ func (page Builder) renderNode(node *Node) { for _, child := range node.children { + c := child.Render() if child == nil { continue } - if child.tag == "class" { - insertAttribute(node, "class", child.value) - child.tag = FlagSkip + if c.tag == "class" { + insertAttribute(node, "class", c.value) + c.tag = FlagSkip } - if child.tag == FlagAttributeList { - for _, gc := range child.children { - for key, value := range gc.attributes { + if c.tag == FlagAttributeList { + for _, gc := range c.children { + gcr := gc.Render() + for key, value := range gcr.attributes { insertAttribute(node, key, value) } - gc.tag = FlagSkip + gcr.tag = FlagSkip } - child.tag = FlagSkip + c.tag = FlagSkip } - if child.tag == "attribute" { - for key, value := range child.attributes { + if c.tag == "attribute" { + for key, value := range c.attributes { insertAttribute(node, key, value) } - child.tag = FlagSkip + c.tag = FlagSkip } } @@ -108,16 +111,18 @@ func (page Builder) renderNode(node *Node) { continue } - if child.tag == FlagText { - page.builder.WriteString(child.text) + c := child.Render() + + if c.tag == FlagText { + page.builder.WriteString(c.text) continue } - if child.tag == FlagRaw { - page.builder.WriteString(child.value) + if c.tag == FlagRaw { + page.builder.WriteString(c.value) continue } - if child.tag != FlagSkip { - page.renderNode(child) + if c.tag != FlagSkip { + page.renderNode(c) } } if node.tag != "" { @@ -125,12 +130,12 @@ func (page Builder) renderNode(node *Node) { } } -func Render(node *Node) string { +func Render(node Renderable) string { start := time.Now() builder := strings.Builder{} page := Builder{ builder: &builder, - root: node, + root: node.Render(), } page.render() d := page.builder.String() diff --git a/h/tag.go b/h/tag.go index f5ac1df..5fcd07a 100644 --- a/h/tag.go +++ b/h/tag.go @@ -13,29 +13,27 @@ type Node struct { id string tag string attributes map[string]string - children []*Node + children []Renderable text string value string changed bool } -type Action struct { - Type string - Target *Node - Value any +func (node *Node) Render() *Node { + return node } -func (node *Node) AppendChild(child *Node) *Node { +func (node *Node) AppendChild(child Renderable) Renderable { node.children = append(node.children, child) return node } -func (node *Node) SetChanged(changed bool) *Node { +func (node *Node) SetChanged(changed bool) Renderable { node.changed = changed return node } -func Data(data map[string]any) *Node { +func Data(data map[string]any) Renderable { serialized, err := json.Marshal(data) if err != nil { return Empty() @@ -43,14 +41,14 @@ func Data(data map[string]any) *Node { return Attribute("x-data", string(serialized)) } -func ClassIf(condition bool, value string) *Node { +func ClassIf(condition bool, value string) Renderable { if condition { return Class(value) } return Empty() } -func Class(value ...string) *Node { +func Class(value ...string) Renderable { return &Node{ tag: "class", value: MergeClasses(value...), @@ -65,37 +63,37 @@ func MergeClasses(classes ...string) string { return builder } -func Id(value string) *Node { +func Id(value string) Renderable { if strings.HasPrefix(value, "#") { value = value[1:] } return Attribute("id", value) } -func Attributes(attrs map[string]string) *Node { +func Attributes(attrs map[string]string) Renderable { return &Node{ tag: "attribute", attributes: attrs, } } -func Attribute(key string, value string) *Node { +func Attribute(key string, value string) Renderable { return Attributes(map[string]string{key: value}) } -func Disabled() *Node { +func Disabled() Renderable { return Attribute("disabled", "") } -func Get(path string) *Node { +func Get(path string) Renderable { return Attribute("hx-get", path) } -func GetPartial(partial func(ctx *fiber.Ctx) *Partial) *Node { +func GetPartial(partial func(ctx *fiber.Ctx) *Partial) Renderable { return Get(GetPartialPath(partial)) } -func GetPartialWithQs(partial func(ctx *fiber.Ctx) *Partial, qs string) *Node { +func GetPartialWithQs(partial func(ctx *fiber.Ctx) *Partial, qs string) Renderable { return Get(GetPartialPathWithQs(partial, qs)) } @@ -107,27 +105,27 @@ type ReloadParams struct { Triggers []string } -func ViewOnLoad(partial func(ctx *fiber.Ctx) *Partial) *Node { +func ViewOnLoad(partial func(ctx *fiber.Ctx) *Partial) Renderable { return View(partial, ReloadParams{ Triggers: CreateTriggers("load"), }) } -func View(partial func(ctx *fiber.Ctx) *Partial, params ReloadParams) *Node { +func View(partial func(ctx *fiber.Ctx) *Partial, params ReloadParams) Renderable { return Div(Attributes(map[string]string{ "hx-get": GetPartialPath(partial), "hx-trigger": strings.Join(params.Triggers, ", "), })) } -func ViewWithTriggers(partial func(ctx *fiber.Ctx) *Partial, triggers ...string) *Node { +func ViewWithTriggers(partial func(ctx *fiber.Ctx) *Partial, triggers ...string) Renderable { return Div(Attributes(map[string]string{ "hx-get": GetPartialPath(partial), "hx-trigger": strings.Join(triggers, ", "), })) } -func GetWithQs(path string, qs map[string]string) *Node { +func GetWithQs(path string, qs map[string]string) Renderable { u, err := url.Parse(path) if err != nil { return Empty() @@ -144,104 +142,104 @@ func GetWithQs(path string, qs map[string]string) *Node { return Get(u.String()) } -func Post(url string) *Node { +func Post(url string) Renderable { return Attribute("hx-post", url) } -func Trigger(trigger string) *Node { +func Trigger(trigger string) Renderable { return Attribute("hx-trigger", trigger) } -func Text(text string) *Node { +func Text(text string) Renderable { return &Node{ tag: "text", text: text, } } -func Pf(format string, args ...interface{}) *Node { +func Pf(format string, args ...interface{}) Renderable { return P(fmt.Sprintf(format, args...)) } -func Target(target string) *Node { +func Target(target string) Renderable { return Attribute("hx-target", target) } -func Name(name string) *Node { +func Name(name string) Renderable { return Attribute("name", name) } -func Confirm(message string) *Node { +func Confirm(message string) Renderable { return Attribute("hx-confirm", message) } -func Href(path string) *Node { +func Href(path string) Renderable { return Attribute("href", path) } -func Type(name string) *Node { +func Type(name string) Renderable { return Attribute("type", name) } -func Placeholder(placeholder string) *Node { +func Placeholder(placeholder string) Renderable { return Attribute("placeholder", placeholder) } -func OutOfBandSwap(selector string) *Node { +func OutOfBandSwap(selector string) Renderable { return Attribute("hx-swap-oob", Ternary(selector == "", "true", selector)) } -func Click(value string) *Node { +func Click(value string) Renderable { return Attribute("onclick", value) } -func Tag(tag string, children ...*Node) *Node { +func Tag(tag string, children ...Renderable) Renderable { return &Node{ tag: tag, children: children, } } -func Html(children ...*Node) *Node { +func Html(children ...Renderable) Renderable { return Tag("html", children...) } -func Head(children ...*Node) *Node { +func Head(children ...Renderable) Renderable { return Tag("head", children...) } -func Body(children ...*Node) *Node { +func Body(children ...Renderable) Renderable { return Tag("body", children...) } -func Script(url string) *Node { +func Script(url string) Renderable { return &Node{ tag: "script", attributes: map[string]string{ "src": url, }, - children: make([]*Node, 0), + children: make([]Renderable, 0), } } -func Raw(text string) *Node { +func Raw(text string) Renderable { return &Node{ tag: "raw", - children: make([]*Node, 0), + children: make([]Renderable, 0), value: text, } } -func RawScript(text string) *Node { +func RawScript(text string) Renderable { return Raw("") } -func Div(children ...*Node) *Node { +func Div(children ...Renderable) Renderable { return Tag("div", children...) } -func Input(inputType string, children ...*Node) *Node { +func Input(inputType string, children ...Renderable) Renderable { return &Node{ tag: "input", attributes: map[string]string{ @@ -251,10 +249,10 @@ func Input(inputType string, children ...*Node) *Node { } } -func List[T any](items []T, mapper func(item T, index int) *Node) *Node { +func List[T any](items []T, mapper func(item T, index int) Renderable) Renderable { node := &Node{ tag: "", - children: make([]*Node, len(items)), + children: make([]Renderable, len(items)), } for index, value := range items { node.children[index] = mapper(value, index) @@ -262,35 +260,35 @@ func List[T any](items []T, mapper func(item T, index int) *Node) *Node { return node } -func Fragment(children ...*Node) *Node { +func Fragment(children ...Renderable) Renderable { return &Node{ tag: "", children: children, } } -func AttributeList(children ...*Node) *Node { +func AttributeList(children ...Renderable) Renderable { return &Node{ tag: FlagAttributeList, children: children, } } -func AppendChildren(node *Node, children ...*Node) *Node { +func AppendChildren(node *Node, children ...Renderable) Renderable { node.children = append(node.children, children...) return node } -func Button(children ...*Node) *Node { +func Button(children ...Renderable) Renderable { return Tag("button", children...) } -func Indicator(tag string) *Node { +func Indicator(tag string) Renderable { return Attribute("hx-indicator", tag) } -func P(text string, children ...*Node) *Node { +func P(text string, children ...Renderable) Renderable { return &Node{ tag: "p", children: children, @@ -298,11 +296,11 @@ func P(text string, children ...*Node) *Node { } } -func Form(children ...*Node) *Node { +func Form(children ...Renderable) Renderable { return Tag("form", children...) } -func A(text string, children ...*Node) *Node { +func A(text string, children ...Renderable) Renderable { return &Node{ tag: "a", children: children, @@ -310,42 +308,42 @@ func A(text string, children ...*Node) *Node { } } -func Nav(children ...*Node) *Node { +func Nav(children ...Renderable) Renderable { return Tag("nav", children...) } -func Empty() *Node { +func Empty() Renderable { return &Node{ tag: "", } } -func BeforeRequestSetHtml(children ...*Node) *Node { +func BeforeRequestSetHtml(children ...Renderable) Renderable { serialized := Render(Fragment(children...)) return Attribute("hx-on::before-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`) } -func BeforeRequestSetAttribute(key string, value string) *Node { +func BeforeRequestSetAttribute(key string, value string) Renderable { return Attribute("hx-on::before-request", `this.setAttribute('`+key+`', '`+value+`')`) } -func BeforeRequestSetText(text string) *Node { +func BeforeRequestSetText(text string) Renderable { return Attribute("hx-on::before-request", `this.innerText = '`+text+`'`) } -func AfterRequestRemoveAttribute(key string, value string) *Node { +func AfterRequestRemoveAttribute(key string, value string) Renderable { return Attribute("hx-on::after-request", `this.removeAttribute('`+key+`')`) } -func IfQueryParam(key string, node *Node) *Node { +func IfQueryParam(key string, node *Node) Renderable { return Fragment(Attribute("hx-if-qp:"+key, "true"), node) } -func Hidden() *Node { +func Hidden() Renderable { return Attribute("style", "display:none") } -func MatchQueryParam(defaultValue string, active string, m map[string]*Node) *Node { +func MatchQueryParam(defaultValue string, active string, m map[string]*Node) Renderable { rendered := make(map[string]string) for s, node := range m { @@ -360,29 +358,29 @@ func MatchQueryParam(defaultValue string, active string, m map[string]*Node) *No ) for s, node := range rendered { - root = AppendChildren(root, Attribute("hx-match-qp-mapping:"+s, ``+html.EscapeString(node)+``)) + root = AppendChildren(root.Render(), Attribute("hx-match-qp-mapping:"+s, ``+html.EscapeString(node)+``)) } return root } -func AfterRequestSetHtml(children ...*Node) *Node { +func AfterRequestSetHtml(children ...Renderable) Renderable { serialized := Render(Fragment(children...)) return Attribute("hx-on::after-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`) } -func Children(children []*Node) *Node { +func Children(children []Renderable) Renderable { return &Node{ tag: FlagChildrenList, children: children, } } -func Label(text string) *Node { +func Label(text string) Renderable { return Tag("label", Text(text)) } -func If(condition bool, node *Node) *Node { +func If(condition bool, node Renderable) Renderable { if condition { return node } else { @@ -390,7 +388,7 @@ func If(condition bool, node *Node) *Node { } } -func IfElse(condition bool, node *Node, node2 *Node) *Node { +func IfElse(condition bool, node Renderable, node2 Renderable) Renderable { if condition { return node } else { @@ -398,7 +396,7 @@ func IfElse(condition bool, node *Node, node2 *Node) *Node { } } -func IfElseLazy(condition bool, cb1 func() *Node, cb2 func() *Node) *Node { +func IfElseLazy(condition bool, cb1 func() Renderable, cb2 func() Renderable) Renderable { if condition { return cb1() } else { @@ -406,7 +404,7 @@ func IfElseLazy(condition bool, cb1 func() *Node, cb2 func() *Node) *Node { } } -func IfHtmxRequest(ctx *fiber.Ctx, node *Node) *Node { +func IfHtmxRequest(ctx *fiber.Ctx, node Renderable) Renderable { if ctx.Get("HX-Request") != "" { return node } @@ -425,25 +423,26 @@ func NewSwap(selector string, content *Node) SwapArg { } } -func Swap(ctx *fiber.Ctx, content *Node) *Node { +func Swap(ctx *fiber.Ctx, content Renderable) Renderable { return SwapWithSelector(ctx, "", content) } -func SwapWithSelector(ctx *fiber.Ctx, selector string, content *Node) *Node { +func SwapWithSelector(ctx *fiber.Ctx, selector string, content Renderable) Renderable { if ctx == nil || ctx.Get("HX-Request") == "" { return Empty() } - return content.AppendChild(OutOfBandSwap(selector)) + c := content.Render() + return c.AppendChild(OutOfBandSwap(selector)) } -func SwapMany(ctx *fiber.Ctx, args ...SwapArg) *Node { +func SwapMany(ctx *fiber.Ctx, args ...SwapArg) Renderable { if ctx.Get("HX-Request") == "" { return Empty() } for _, arg := range args { arg.Content.AppendChild(OutOfBandSwap(arg.Selector)) } - return Fragment(Map(args, func(arg SwapArg) *Node { + return Fragment(Map(args, func(arg SwapArg) Renderable { return arg.Content })...) } @@ -456,7 +455,7 @@ type OnRequestSwapArgs struct { AfterRequest *Node } -func OnRequestSwap(args OnRequestSwapArgs) *Node { +func OnRequestSwap(args OnRequestSwapArgs) Renderable { return Div(args.Default, BeforeRequestSetHtml(args.BeforeRequest), AfterRequestSetHtml(args.AfterRequest), diff --git a/h/util.go b/h/util.go index d4d7644..cdc7a4c 100644 --- a/h/util.go +++ b/h/util.go @@ -6,6 +6,10 @@ import ( "net/url" ) +type Renderable interface { + Render() *Node +} + func Ternary[T any](value bool, a T, b T) T { if value { return a diff --git a/news/views.go b/news/views.go index 7da5836..34713fe 100644 --- a/news/views.go +++ b/news/views.go @@ -7,7 +7,7 @@ import ( "time" ) -func StoryList() *h.Node { +func StoryList() h.Renderable { posts, _ := database.GetOrSet[[]Post]("posts", func() []Post { p, _ := List() @@ -21,13 +21,13 @@ func StoryList() *h.Node { } return h.Fragment( - h.Div(h.List(*posts, func(item Post, index int) *h.Node { + h.Div(h.List(*posts, func(item Post, index int) h.Renderable { return StoryCard(item) })), ) } -func StoryCard(post Post) *h.Node { +func StoryCard(post Post) h.Renderable { url := fmt.Sprintf("/news/%d", post.Id) return h.Div( h.Class("items-center bg-indigo-200 p-4 rounded"), @@ -35,7 +35,7 @@ func StoryCard(post Post) *h.Node { ) } -func StoryFull(id string) *h.Node { +func StoryFull(id string) h.Renderable { post, err := Get(id) if err != nil { return h.P(err.Error()) diff --git a/pages/base/root.go b/pages/base/root.go index 6e2f822..32ccc08 100644 --- a/pages/base/root.go +++ b/pages/base/root.go @@ -6,7 +6,7 @@ import ( "mhtml/partials/sheet" ) -func RootPage(children ...*h.Node) *h.Node { +func RootPage(children ...h.Renderable) h.Renderable { return h.Html( h.Head( h.Script("https://cdn.tailwindcss.com"), diff --git a/pages/news.index.go b/pages/news.index.go index 23e3a2b..740d6d0 100644 --- a/pages/news.index.go +++ b/pages/news.index.go @@ -13,7 +13,7 @@ func ListPage(ctx *fiber.Ctx) *h.Page { )) } -func list(ctx *fiber.Ctx) *h.Node { +func list(ctx *fiber.Ctx) h.Renderable { return h.Fragment( h.ViewOnLoad(partials.NewsSheet), h.Div( diff --git a/partials/button.go b/partials/button.go index 24796a7..f0a7512 100644 --- a/partials/button.go +++ b/partials/button.go @@ -5,7 +5,7 @@ import ( "mhtml/ui" ) -func OpenSheetButton(open bool, children ...*h.Node) *h.Node { +func OpenSheetButton(open bool, children ...h.Renderable) h.Renderable { if open { return ui.PrimaryButton(ui.ButtonProps{ Id: "open-sheet", diff --git a/partials/nav.go b/partials/nav.go index 1c52e50..4528d82 100644 --- a/partials/nav.go +++ b/partials/nav.go @@ -7,7 +7,7 @@ type Link struct { Path string } -func NavBar() *h.Node { +func NavBar() h.Renderable { links := []Link{ {"Home", "/"}, @@ -17,7 +17,7 @@ func NavBar() *h.Node { return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"), h.Children( - h.Map(links, func(link Link) *h.Node { + h.Map(links, func(link Link) h.Renderable { return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400")) }), )) diff --git a/partials/news.go b/partials/news.go index 6617613..27238e8 100644 --- a/partials/news.go +++ b/partials/news.go @@ -43,15 +43,15 @@ func NewsSheetOpenCount(ctx *fiber.Ctx) *h.Partial { ) } -func SheetWrapper(children ...*h.Node) *h.Node { +func SheetWrapper(children ...h.Renderable) h.Renderable { return h.Div(h.Id("sheet-partial"), h.Fragment(children...)) } -func SheetClosed() *h.Node { +func SheetClosed() h.Renderable { return h.Div() } -func SheetOpen() *h.Node { +func SheetOpen() h.Renderable { return h.Fragment(h.Div( h.Class(`fixed top-0 right-0 h-full w-96 bg-gray-100 shadow-lg z-50`), h.Div( diff --git a/partials/patient/patient.go b/partials/patient/patient.go index 8c97910..42ce5a3 100644 --- a/partials/patient/patient.go +++ b/partials/patient/patient.go @@ -52,7 +52,7 @@ func AddPatientSheet(ctx *fiber.Ctx) *h.Partial { })) } -func addPatientForm() *h.Node { +func addPatientForm() h.Renderable { return h.Form( h.Post(h.GetPartialPath(Create)), h.Class("flex flex-col gap-2"), @@ -84,7 +84,7 @@ func addPatientForm() *h.Node { ) } -func Row(patient *Patient, index int) *h.Node { +func Row(patient *Patient, index int) h.Renderable { return h.Div( h.Class("flex flex-col gap-2 rounded p-4", h.Ternary(index%2 == 0, "bg-red-100", "")), h.Pf("Name: %s", patient.Name), @@ -92,7 +92,7 @@ func Row(patient *Patient, index int) *h.Node { ) } -func AddPatientButton() *h.Node { +func AddPatientButton() h.Renderable { return ui.Button(ui.ButtonProps{ Id: "add-patient", Text: "Add Patient", diff --git a/partials/sheet/sheet.go b/partials/sheet/sheet.go index 3bc5045..57be992 100644 --- a/partials/sheet/sheet.go +++ b/partials/sheet/sheet.go @@ -7,12 +7,12 @@ import ( type Props struct { ClassName string - Root *h.Node + Root h.Renderable } var Id = "#active-modal" -func Opened(props Props) *h.Node { +func Opened(props Props) h.Renderable { return h.Fragment(h.Div( h.Class(`fixed top-0 right-0 h-full shadow-lg z-50`, h.Ternary(props.ClassName != "", props.ClassName, "w-96 bg-gray-100")), @@ -22,7 +22,7 @@ func Opened(props Props) *h.Node { ))) } -func Closed() *h.Node { +func Closed() h.Renderable { return h.Div(h.Id(Id)) } @@ -32,7 +32,7 @@ func Close(ctx *fiber.Ctx) *h.Partial { ) } -func closeButton() *h.Node { +func closeButton() h.Renderable { return h.Div( h.Class("absolute top-0 right-0 p-3"), h.Button( diff --git a/sandbox.go b/sandbox.go deleted file mode 100644 index 2ebefa7..0000000 --- a/sandbox.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import "mhtml/h" - -func init() { - h.Div( - h.Id("sandbox"), - h.Button( - h.Id("btn"), - h.Class("bg-blue-500 text-white p-2"), - ), - ) - - h.Html( - h.Head(), - h.Body( - h.Nav( - h.Class("flex gap-4 items-center p-4 text-slate-600 "), - h.A(h.Href("/"), h.Class("cursor-pointer hover:text-blue-400 ")), - h.A(h.Class("cursor-pointer hover:text-blue-400 "), - h.Href("/news")), h.A( - h.Href("/patients"), - h.Class("cursor-pointer hover:text-blue-400 "))), - h.Div( - h.Id("active-modal")), - h.Div(h.Class("flex flex-col gap-2 bg-white h-full "), - h.Div(h.Class("flex flex-col p-4 w-full "), - h.Div(h.Div(h.Class("flex justify-between items-center "), - h.P( - h.Class("text-lg font-bold ")), - h.Button(h.HxTarget("#active-modal"), - h.Type("button"), - h.Id("add-patient"), - h.Class("flex gap-1 items-center border p-4 rounded cursor-hover bg-blue-700 text-white rounded p-2 h-12 "), - h.HxGet("mhtml/partials/patient.AddPatientSheet"))), - h.Div(h.HxGet("mhtml/partials/patient.List"), - h.HxTrigger("load, patient-added from:body"), - h.Class(""), h.Div(h.Class("mt-8"), - h.Id("patient-list"), - h.Div(h.Class("flex flex-col gap-2 rounded p-4 bg-red-100 "), - h.P(), - h.P()), - ), - ), - ), - ), - ), - h.Div( - h.HxGet("/livereload"), - h.HxTrigger("every 200ms"), - h.Class(""), - ), - )) - -} diff --git a/ui/button.go b/ui/button.go index b75410c..14498de 100644 --- a/ui/button.go +++ b/ui/button.go @@ -12,20 +12,20 @@ type ButtonProps struct { Trigger string Get string Class string - Children []*h.Node + Children []h.Renderable } -func PrimaryButton(props ButtonProps) *h.Node { +func PrimaryButton(props ButtonProps) h.Renderable { props.Class = h.MergeClasses(props.Class, "border-blue-700 bg-blue-700 text-white") return Button(props) } -func SecondaryButton(props ButtonProps) *h.Node { +func SecondaryButton(props ButtonProps) h.Renderable { props.Class = h.MergeClasses(props.Class, "border-gray-700 bg-gray-700 text-white") return Button(props) } -func Button(props ButtonProps) *h.Node { +func Button(props ButtonProps) h.Renderable { text := h.Text(props.Text) diff --git a/ui/input.go b/ui/input.go index 5d4be71..e1d5b2f 100644 --- a/ui/input.go +++ b/ui/input.go @@ -10,7 +10,7 @@ type InputProps struct { DefaultValue string } -func Input(props InputProps) *h.Node { +func Input(props InputProps) h.Renderable { input := h.Input( props.Type, h.Class("border p-2 rounded"),