replace node everywhere with renderable

This commit is contained in:
maddalax 2024-09-11 18:41:21 -05:00
parent de28a6a783
commit fec5558f28
17 changed files with 145 additions and 188 deletions

View file

@ -39,7 +39,7 @@ func (a App) start(app *fiber.App) {
} }
func HtmlView(c *fiber.Ctx, page *Page) error { func HtmlView(c *fiber.Ctx, page *Page) error {
root := page.Root root := page.Root.Render()
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML) c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
if GetApp().LiveReload && root.tag == "html" { if GetApp().LiveReload && root.tag == "html" {

View file

@ -15,23 +15,27 @@ type Partial struct {
Root *Node Root *Node
} }
func (p *Partial) Render() *Node {
return p.Root
}
func (p *Partial) ToNode() *Node { func (p *Partial) ToNode() *Node {
return p.Root return p.Root
} }
type Page struct { type Page struct {
Root *Node Root Renderable
HttpMethod string HttpMethod string
} }
func NewPage(root *Node) *Page { func NewPage(root Renderable) *Page {
return &Page{ return &Page{
HttpMethod: http.MethodGet, HttpMethod: http.MethodGet,
Root: root, Root: root,
} }
} }
func NewPageWithHttpMethod(httpMethod string, root *Node) *Page { func NewPageWithHttpMethod(httpMethod string, root Renderable) *Page {
return &Page{ return &Page{
HttpMethod: httpMethod, HttpMethod: httpMethod,
Root: root, Root: root,
@ -42,16 +46,16 @@ func WrapPartial(ctx *fiber.Ctx, cb func(ctx *fiber.Ctx) *Partial) *Node {
return cb(ctx).Root return cb(ctx).Root
} }
func NewPartialWithHeaders(headers *Headers, root *Node) *Partial { func NewPartialWithHeaders(headers *Headers, root Renderable) *Partial {
return &Partial{ return &Partial{
Headers: headers, Headers: headers,
Root: root, Root: root.Render(),
} }
} }
func NewPartial(root *Node) *Partial { func NewPartial(root Renderable) *Partial {
return &Partial{ return &Partial{
Root: root, Root: root.Render(),
} }
} }

View file

@ -24,7 +24,7 @@ func LiveReloadHandler(c *fiber.Ctx) error {
return c.SendString("") return c.SendString("")
} }
func LiveReload() *Node { func LiveReload() Renderable {
return Div(Get("/livereload"), Trigger("every 200ms")) return Div(Get("/livereload"), Trigger("every 200ms"))
} }

View file

@ -39,14 +39,15 @@ func (page Builder) renderNode(node *Node) {
node.attributes = map[string]string{} node.attributes = map[string]string{}
} }
flatChildren := make([]*Node, 0) flatChildren := make([]Renderable, 0)
for _, child := range node.children { for _, child := range node.children {
c := child.Render()
flatChildren = append(flatChildren, child) flatChildren = append(flatChildren, child)
if child.tag == FlagChildrenList { if c.tag == FlagChildrenList {
for _, gc := range child.children { for _, gc := range c.children {
flatChildren = append(flatChildren, gc) 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 { for _, child := range node.children {
c := child.Render()
if child == nil { if child == nil {
continue continue
} }
if child.tag == "class" { if c.tag == "class" {
insertAttribute(node, "class", child.value) insertAttribute(node, "class", c.value)
child.tag = FlagSkip c.tag = FlagSkip
} }
if child.tag == FlagAttributeList { if c.tag == FlagAttributeList {
for _, gc := range child.children { for _, gc := range c.children {
for key, value := range gc.attributes { gcr := gc.Render()
for key, value := range gcr.attributes {
insertAttribute(node, key, value) insertAttribute(node, key, value)
} }
gc.tag = FlagSkip gcr.tag = FlagSkip
} }
child.tag = FlagSkip c.tag = FlagSkip
} }
if child.tag == "attribute" { if c.tag == "attribute" {
for key, value := range child.attributes { for key, value := range c.attributes {
insertAttribute(node, key, value) insertAttribute(node, key, value)
} }
child.tag = FlagSkip c.tag = FlagSkip
} }
} }
@ -108,16 +111,18 @@ func (page Builder) renderNode(node *Node) {
continue continue
} }
if child.tag == FlagText { c := child.Render()
page.builder.WriteString(child.text)
if c.tag == FlagText {
page.builder.WriteString(c.text)
continue continue
} }
if child.tag == FlagRaw { if c.tag == FlagRaw {
page.builder.WriteString(child.value) page.builder.WriteString(c.value)
continue continue
} }
if child.tag != FlagSkip { if c.tag != FlagSkip {
page.renderNode(child) page.renderNode(c)
} }
} }
if node.tag != "" { 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() start := time.Now()
builder := strings.Builder{} builder := strings.Builder{}
page := Builder{ page := Builder{
builder: &builder, builder: &builder,
root: node, root: node.Render(),
} }
page.render() page.render()
d := page.builder.String() d := page.builder.String()

153
h/tag.go
View file

@ -13,29 +13,27 @@ type Node struct {
id string id string
tag string tag string
attributes map[string]string attributes map[string]string
children []*Node children []Renderable
text string text string
value string value string
changed bool changed bool
} }
type Action struct { func (node *Node) Render() *Node {
Type string return node
Target *Node
Value any
} }
func (node *Node) AppendChild(child *Node) *Node { func (node *Node) AppendChild(child Renderable) Renderable {
node.children = append(node.children, child) node.children = append(node.children, child)
return node return node
} }
func (node *Node) SetChanged(changed bool) *Node { func (node *Node) SetChanged(changed bool) Renderable {
node.changed = changed node.changed = changed
return node return node
} }
func Data(data map[string]any) *Node { func Data(data map[string]any) Renderable {
serialized, err := json.Marshal(data) serialized, err := json.Marshal(data)
if err != nil { if err != nil {
return Empty() return Empty()
@ -43,14 +41,14 @@ func Data(data map[string]any) *Node {
return Attribute("x-data", string(serialized)) return Attribute("x-data", string(serialized))
} }
func ClassIf(condition bool, value string) *Node { func ClassIf(condition bool, value string) Renderable {
if condition { if condition {
return Class(value) return Class(value)
} }
return Empty() return Empty()
} }
func Class(value ...string) *Node { func Class(value ...string) Renderable {
return &Node{ return &Node{
tag: "class", tag: "class",
value: MergeClasses(value...), value: MergeClasses(value...),
@ -65,37 +63,37 @@ func MergeClasses(classes ...string) string {
return builder return builder
} }
func Id(value string) *Node { func Id(value string) Renderable {
if strings.HasPrefix(value, "#") { if strings.HasPrefix(value, "#") {
value = value[1:] value = value[1:]
} }
return Attribute("id", value) return Attribute("id", value)
} }
func Attributes(attrs map[string]string) *Node { func Attributes(attrs map[string]string) Renderable {
return &Node{ return &Node{
tag: "attribute", tag: "attribute",
attributes: attrs, attributes: attrs,
} }
} }
func Attribute(key string, value string) *Node { func Attribute(key string, value string) Renderable {
return Attributes(map[string]string{key: value}) return Attributes(map[string]string{key: value})
} }
func Disabled() *Node { func Disabled() Renderable {
return Attribute("disabled", "") return Attribute("disabled", "")
} }
func Get(path string) *Node { func Get(path string) Renderable {
return Attribute("hx-get", path) 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)) 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)) return Get(GetPartialPathWithQs(partial, qs))
} }
@ -107,27 +105,27 @@ type ReloadParams struct {
Triggers []string Triggers []string
} }
func ViewOnLoad(partial func(ctx *fiber.Ctx) *Partial) *Node { func ViewOnLoad(partial func(ctx *fiber.Ctx) *Partial) Renderable {
return View(partial, ReloadParams{ return View(partial, ReloadParams{
Triggers: CreateTriggers("load"), 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{ return Div(Attributes(map[string]string{
"hx-get": GetPartialPath(partial), "hx-get": GetPartialPath(partial),
"hx-trigger": strings.Join(params.Triggers, ", "), "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{ return Div(Attributes(map[string]string{
"hx-get": GetPartialPath(partial), "hx-get": GetPartialPath(partial),
"hx-trigger": strings.Join(triggers, ", "), "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) u, err := url.Parse(path)
if err != nil { if err != nil {
return Empty() return Empty()
@ -144,104 +142,104 @@ func GetWithQs(path string, qs map[string]string) *Node {
return Get(u.String()) return Get(u.String())
} }
func Post(url string) *Node { func Post(url string) Renderable {
return Attribute("hx-post", url) return Attribute("hx-post", url)
} }
func Trigger(trigger string) *Node { func Trigger(trigger string) Renderable {
return Attribute("hx-trigger", trigger) return Attribute("hx-trigger", trigger)
} }
func Text(text string) *Node { func Text(text string) Renderable {
return &Node{ return &Node{
tag: "text", tag: "text",
text: text, text: text,
} }
} }
func Pf(format string, args ...interface{}) *Node { func Pf(format string, args ...interface{}) Renderable {
return P(fmt.Sprintf(format, args...)) return P(fmt.Sprintf(format, args...))
} }
func Target(target string) *Node { func Target(target string) Renderable {
return Attribute("hx-target", target) return Attribute("hx-target", target)
} }
func Name(name string) *Node { func Name(name string) Renderable {
return Attribute("name", name) return Attribute("name", name)
} }
func Confirm(message string) *Node { func Confirm(message string) Renderable {
return Attribute("hx-confirm", message) return Attribute("hx-confirm", message)
} }
func Href(path string) *Node { func Href(path string) Renderable {
return Attribute("href", path) return Attribute("href", path)
} }
func Type(name string) *Node { func Type(name string) Renderable {
return Attribute("type", name) return Attribute("type", name)
} }
func Placeholder(placeholder string) *Node { func Placeholder(placeholder string) Renderable {
return Attribute("placeholder", placeholder) return Attribute("placeholder", placeholder)
} }
func OutOfBandSwap(selector string) *Node { func OutOfBandSwap(selector string) Renderable {
return Attribute("hx-swap-oob", return Attribute("hx-swap-oob",
Ternary(selector == "", "true", selector)) Ternary(selector == "", "true", selector))
} }
func Click(value string) *Node { func Click(value string) Renderable {
return Attribute("onclick", value) return Attribute("onclick", value)
} }
func Tag(tag string, children ...*Node) *Node { func Tag(tag string, children ...Renderable) Renderable {
return &Node{ return &Node{
tag: tag, tag: tag,
children: children, children: children,
} }
} }
func Html(children ...*Node) *Node { func Html(children ...Renderable) Renderable {
return Tag("html", children...) return Tag("html", children...)
} }
func Head(children ...*Node) *Node { func Head(children ...Renderable) Renderable {
return Tag("head", children...) return Tag("head", children...)
} }
func Body(children ...*Node) *Node { func Body(children ...Renderable) Renderable {
return Tag("body", children...) return Tag("body", children...)
} }
func Script(url string) *Node { func Script(url string) Renderable {
return &Node{ return &Node{
tag: "script", tag: "script",
attributes: map[string]string{ attributes: map[string]string{
"src": url, "src": url,
}, },
children: make([]*Node, 0), children: make([]Renderable, 0),
} }
} }
func Raw(text string) *Node { func Raw(text string) Renderable {
return &Node{ return &Node{
tag: "raw", tag: "raw",
children: make([]*Node, 0), children: make([]Renderable, 0),
value: text, value: text,
} }
} }
func RawScript(text string) *Node { func RawScript(text string) Renderable {
return Raw("<script>" + text + "</script>") return Raw("<script>" + text + "</script>")
} }
func Div(children ...*Node) *Node { func Div(children ...Renderable) Renderable {
return Tag("div", children...) return Tag("div", children...)
} }
func Input(inputType string, children ...*Node) *Node { func Input(inputType string, children ...Renderable) Renderable {
return &Node{ return &Node{
tag: "input", tag: "input",
attributes: map[string]string{ 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{ node := &Node{
tag: "", tag: "",
children: make([]*Node, len(items)), children: make([]Renderable, len(items)),
} }
for index, value := range items { for index, value := range items {
node.children[index] = mapper(value, index) 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 return node
} }
func Fragment(children ...*Node) *Node { func Fragment(children ...Renderable) Renderable {
return &Node{ return &Node{
tag: "", tag: "",
children: children, children: children,
} }
} }
func AttributeList(children ...*Node) *Node { func AttributeList(children ...Renderable) Renderable {
return &Node{ return &Node{
tag: FlagAttributeList, tag: FlagAttributeList,
children: children, children: children,
} }
} }
func AppendChildren(node *Node, children ...*Node) *Node { func AppendChildren(node *Node, children ...Renderable) Renderable {
node.children = append(node.children, children...) node.children = append(node.children, children...)
return node return node
} }
func Button(children ...*Node) *Node { func Button(children ...Renderable) Renderable {
return Tag("button", children...) return Tag("button", children...)
} }
func Indicator(tag string) *Node { func Indicator(tag string) Renderable {
return Attribute("hx-indicator", tag) return Attribute("hx-indicator", tag)
} }
func P(text string, children ...*Node) *Node { func P(text string, children ...Renderable) Renderable {
return &Node{ return &Node{
tag: "p", tag: "p",
children: children, 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...) return Tag("form", children...)
} }
func A(text string, children ...*Node) *Node { func A(text string, children ...Renderable) Renderable {
return &Node{ return &Node{
tag: "a", tag: "a",
children: children, 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...) return Tag("nav", children...)
} }
func Empty() *Node { func Empty() Renderable {
return &Node{ return &Node{
tag: "", tag: "",
} }
} }
func BeforeRequestSetHtml(children ...*Node) *Node { func BeforeRequestSetHtml(children ...Renderable) Renderable {
serialized := Render(Fragment(children...)) serialized := Render(Fragment(children...))
return Attribute("hx-on::before-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`) 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+`')`) 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+`'`) 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+`')`) 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) return Fragment(Attribute("hx-if-qp:"+key, "true"), node)
} }
func Hidden() *Node { func Hidden() Renderable {
return Attribute("style", "display:none") 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) rendered := make(map[string]string)
for s, node := range m { 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 { 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 return root
} }
func AfterRequestSetHtml(children ...*Node) *Node { func AfterRequestSetHtml(children ...Renderable) Renderable {
serialized := Render(Fragment(children...)) serialized := Render(Fragment(children...))
return Attribute("hx-on::after-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`) return Attribute("hx-on::after-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`)
} }
func Children(children []*Node) *Node { func Children(children []Renderable) Renderable {
return &Node{ return &Node{
tag: FlagChildrenList, tag: FlagChildrenList,
children: children, children: children,
} }
} }
func Label(text string) *Node { func Label(text string) Renderable {
return Tag("label", Text(text)) return Tag("label", Text(text))
} }
func If(condition bool, node *Node) *Node { func If(condition bool, node Renderable) Renderable {
if condition { if condition {
return node return node
} else { } 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 { if condition {
return node return node
} else { } 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 { if condition {
return cb1() return cb1()
} else { } 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") != "" { if ctx.Get("HX-Request") != "" {
return node 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) 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") == "" { if ctx == nil || ctx.Get("HX-Request") == "" {
return Empty() 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") == "" { if ctx.Get("HX-Request") == "" {
return Empty() return Empty()
} }
for _, arg := range args { for _, arg := range args {
arg.Content.AppendChild(OutOfBandSwap(arg.Selector)) 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 return arg.Content
})...) })...)
} }
@ -456,7 +455,7 @@ type OnRequestSwapArgs struct {
AfterRequest *Node AfterRequest *Node
} }
func OnRequestSwap(args OnRequestSwapArgs) *Node { func OnRequestSwap(args OnRequestSwapArgs) Renderable {
return Div(args.Default, return Div(args.Default,
BeforeRequestSetHtml(args.BeforeRequest), BeforeRequestSetHtml(args.BeforeRequest),
AfterRequestSetHtml(args.AfterRequest), AfterRequestSetHtml(args.AfterRequest),

View file

@ -6,6 +6,10 @@ import (
"net/url" "net/url"
) )
type Renderable interface {
Render() *Node
}
func Ternary[T any](value bool, a T, b T) T { func Ternary[T any](value bool, a T, b T) T {
if value { if value {
return a return a

View file

@ -7,7 +7,7 @@ import (
"time" "time"
) )
func StoryList() *h.Node { func StoryList() h.Renderable {
posts, _ := database.GetOrSet[[]Post]("posts", func() []Post { posts, _ := database.GetOrSet[[]Post]("posts", func() []Post {
p, _ := List() p, _ := List()
@ -21,13 +21,13 @@ func StoryList() *h.Node {
} }
return h.Fragment( 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) return StoryCard(item)
})), })),
) )
} }
func StoryCard(post Post) *h.Node { func StoryCard(post Post) h.Renderable {
url := fmt.Sprintf("/news/%d", post.Id) url := fmt.Sprintf("/news/%d", post.Id)
return h.Div( return h.Div(
h.Class("items-center bg-indigo-200 p-4 rounded"), 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) post, err := Get(id)
if err != nil { if err != nil {
return h.P(err.Error()) return h.P(err.Error())

View file

@ -6,7 +6,7 @@ import (
"mhtml/partials/sheet" "mhtml/partials/sheet"
) )
func RootPage(children ...*h.Node) *h.Node { func RootPage(children ...h.Renderable) h.Renderable {
return h.Html( return h.Html(
h.Head( h.Head(
h.Script("https://cdn.tailwindcss.com"), h.Script("https://cdn.tailwindcss.com"),

View file

@ -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( return h.Fragment(
h.ViewOnLoad(partials.NewsSheet), h.ViewOnLoad(partials.NewsSheet),
h.Div( h.Div(

View file

@ -5,7 +5,7 @@ import (
"mhtml/ui" "mhtml/ui"
) )
func OpenSheetButton(open bool, children ...*h.Node) *h.Node { func OpenSheetButton(open bool, children ...h.Renderable) h.Renderable {
if open { if open {
return ui.PrimaryButton(ui.ButtonProps{ return ui.PrimaryButton(ui.ButtonProps{
Id: "open-sheet", Id: "open-sheet",

View file

@ -7,7 +7,7 @@ type Link struct {
Path string Path string
} }
func NavBar() *h.Node { func NavBar() h.Renderable {
links := []Link{ links := []Link{
{"Home", "/"}, {"Home", "/"},
@ -17,7 +17,7 @@ func NavBar() *h.Node {
return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"), return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"),
h.Children( 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")) return h.A(link.Name, h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
}), }),
)) ))

View file

@ -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...)) return h.Div(h.Id("sheet-partial"), h.Fragment(children...))
} }
func SheetClosed() *h.Node { func SheetClosed() h.Renderable {
return h.Div() return h.Div()
} }
func SheetOpen() *h.Node { func SheetOpen() h.Renderable {
return h.Fragment(h.Div( return h.Fragment(h.Div(
h.Class(`fixed top-0 right-0 h-full w-96 bg-gray-100 shadow-lg z-50`), h.Class(`fixed top-0 right-0 h-full w-96 bg-gray-100 shadow-lg z-50`),
h.Div( h.Div(

View file

@ -52,7 +52,7 @@ func AddPatientSheet(ctx *fiber.Ctx) *h.Partial {
})) }))
} }
func addPatientForm() *h.Node { func addPatientForm() h.Renderable {
return h.Form( return h.Form(
h.Post(h.GetPartialPath(Create)), h.Post(h.GetPartialPath(Create)),
h.Class("flex flex-col gap-2"), 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( return h.Div(
h.Class("flex flex-col gap-2 rounded p-4", h.Ternary(index%2 == 0, "bg-red-100", "")), h.Class("flex flex-col gap-2 rounded p-4", h.Ternary(index%2 == 0, "bg-red-100", "")),
h.Pf("Name: %s", patient.Name), 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{ return ui.Button(ui.ButtonProps{
Id: "add-patient", Id: "add-patient",
Text: "Add Patient", Text: "Add Patient",

View file

@ -7,12 +7,12 @@ import (
type Props struct { type Props struct {
ClassName string ClassName string
Root *h.Node Root h.Renderable
} }
var Id = "#active-modal" var Id = "#active-modal"
func Opened(props Props) *h.Node { func Opened(props Props) h.Renderable {
return h.Fragment(h.Div( return h.Fragment(h.Div(
h.Class(`fixed top-0 right-0 h-full shadow-lg z-50`, h.Class(`fixed top-0 right-0 h-full shadow-lg z-50`,
h.Ternary(props.ClassName != "", props.ClassName, "w-96 bg-gray-100")), 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)) 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( return h.Div(
h.Class("absolute top-0 right-0 p-3"), h.Class("absolute top-0 right-0 p-3"),
h.Button( h.Button(

View file

@ -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(""),
),
))
}

View file

@ -12,20 +12,20 @@ type ButtonProps struct {
Trigger string Trigger string
Get string Get string
Class 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") props.Class = h.MergeClasses(props.Class, "border-blue-700 bg-blue-700 text-white")
return Button(props) 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") props.Class = h.MergeClasses(props.Class, "border-gray-700 bg-gray-700 text-white")
return Button(props) return Button(props)
} }
func Button(props ButtonProps) *h.Node { func Button(props ButtonProps) h.Renderable {
text := h.Text(props.Text) text := h.Text(props.Text)

View file

@ -10,7 +10,7 @@ type InputProps struct {
DefaultValue string DefaultValue string
} }
func Input(props InputProps) *h.Node { func Input(props InputProps) h.Renderable {
input := h.Input( input := h.Input(
props.Type, props.Type,
h.Class("border p-2 rounded"), h.Class("border p-2 rounded"),