rewrite the renderer to be a bit more modular
This commit is contained in:
parent
43a5001d98
commit
faf33ef93d
26 changed files with 432 additions and 422 deletions
|
|
@ -10,20 +10,20 @@ type ButtonProps struct {
|
|||
Trigger string
|
||||
Get string
|
||||
Class string
|
||||
Children []h.Renderable
|
||||
Children []h.Ren
|
||||
}
|
||||
|
||||
func PrimaryButton(props ButtonProps) h.Renderable {
|
||||
func PrimaryButton(props ButtonProps) h.Ren {
|
||||
props.Class = h.MergeClasses(props.Class, "border-blue-700 bg-blue-700 text-white")
|
||||
return Button(props)
|
||||
}
|
||||
|
||||
func SecondaryButton(props ButtonProps) h.Renderable {
|
||||
func SecondaryButton(props ButtonProps) h.Ren {
|
||||
props.Class = h.MergeClasses(props.Class, "border-gray-700 bg-gray-700 text-white")
|
||||
return Button(props)
|
||||
}
|
||||
|
||||
func Button(props ButtonProps) h.Renderable {
|
||||
func Button(props ButtonProps) h.Ren {
|
||||
|
||||
text := h.Text(props.Text)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ type InputProps struct {
|
|||
Type string
|
||||
DefaultValue string
|
||||
ValidationPath string
|
||||
Children []h.Renderable
|
||||
Children []h.Ren
|
||||
}
|
||||
|
||||
func Input(props InputProps) h.Renderable {
|
||||
func Input(props InputProps) h.Ren {
|
||||
validation := h.If(props.ValidationPath != "", h.Children(
|
||||
h.Post(props.ValidationPath),
|
||||
h.Trigger("change"),
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ func (a App) start() {
|
|||
}
|
||||
|
||||
func HtmlView(c echo.Context, page *Page) error {
|
||||
root := page.Root.Render()
|
||||
root := page.Root
|
||||
return c.HTML(200,
|
||||
Render(
|
||||
root,
|
||||
|
|
@ -111,7 +111,7 @@ func PartialViewWithHeaders(c echo.Context, headers *Headers, partial *Partial)
|
|||
|
||||
return c.HTML(200,
|
||||
Render(
|
||||
partial.Root,
|
||||
partial,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ func PartialView(c echo.Context, partial *Partial) error {
|
|||
|
||||
return c.HTML(200,
|
||||
Render(
|
||||
partial.Root,
|
||||
partial,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
43
framework/h/attribute.go
Normal file
43
framework/h/attribute.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package h
|
||||
|
||||
type AttributeR struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewAttribute(name string, value string) *AttributeR {
|
||||
return &AttributeR{
|
||||
Name: name,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
type TextContent struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
func NewTextContent(content string) *TextContent {
|
||||
return &TextContent{
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
type RawContent struct {
|
||||
Content string
|
||||
}
|
||||
|
||||
func NewRawContent(content string) *RawContent {
|
||||
return &RawContent{
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
type ChildList struct {
|
||||
Children []Ren
|
||||
}
|
||||
|
||||
func NewChildList(children ...Ren) *ChildList {
|
||||
return &ChildList{
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
|
|
@ -11,40 +11,40 @@ type Headers = map[string]string
|
|||
|
||||
type Partial struct {
|
||||
Headers *Headers
|
||||
Root *Node
|
||||
Root string
|
||||
}
|
||||
|
||||
func (p *Partial) Render() *Node {
|
||||
func (p *Partial) Render() string {
|
||||
return p.Root
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Root Renderable
|
||||
Root Ren
|
||||
HttpMethod string
|
||||
}
|
||||
|
||||
func NewPage(root Renderable) *Page {
|
||||
func NewPage(root Ren) *Page {
|
||||
return &Page{
|
||||
HttpMethod: http.MethodGet,
|
||||
Root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPageWithHttpMethod(httpMethod string, root Renderable) *Page {
|
||||
func NewPageWithHttpMethod(httpMethod string, root Ren) *Page {
|
||||
return &Page{
|
||||
HttpMethod: httpMethod,
|
||||
Root: root,
|
||||
}
|
||||
}
|
||||
|
||||
func NewPartialWithHeaders(headers *Headers, root Renderable) *Partial {
|
||||
func NewPartialWithHeaders(headers *Headers, root Ren) *Partial {
|
||||
return &Partial{
|
||||
Headers: headers,
|
||||
Root: root.Render(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewPartial(root Renderable) *Partial {
|
||||
func NewPartial(root Ren) *Partial {
|
||||
return &Partial{
|
||||
Root: root.Render(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package h
|
||||
|
||||
type HxEvent = string
|
||||
type HxTriggerName = string
|
||||
|
||||
var (
|
||||
HxBeforeRequest HxEvent = "hx-on::before-request"
|
||||
|
|
@ -13,3 +14,11 @@ var (
|
|||
HxRequestStart HxEvent = "hx-on::xhr:loadstart"
|
||||
HxRequestProgress HxEvent = "hx-on::xhr:progress"
|
||||
)
|
||||
|
||||
const (
|
||||
TriggerLoad HxTriggerName = "load"
|
||||
TriggerClick HxTriggerName = "click"
|
||||
TriggerDblClick HxTriggerName = "dblclick"
|
||||
TriggerKeyUpEnter HxTriggerName = "keyup[keyCode==13]"
|
||||
TriggerBlur HxTriggerName = "blur"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -53,25 +53,6 @@ func (l *LifeCycle) OnMutationError(cmd ...JsCommand) *LifeCycle {
|
|||
return l
|
||||
}
|
||||
|
||||
func (l *LifeCycle) Render() *Node {
|
||||
m := make(map[string]string)
|
||||
|
||||
for event, commands := range l.handlers {
|
||||
m[event] = ""
|
||||
for _, command := range commands {
|
||||
m[event] += fmt.Sprintf("%s;", command.Command)
|
||||
}
|
||||
}
|
||||
|
||||
children := make([]Renderable, 0)
|
||||
|
||||
for event, js := range m {
|
||||
children = append(children, Attribute(event, js))
|
||||
}
|
||||
|
||||
return Children(children...).Render()
|
||||
}
|
||||
|
||||
type JsCommand struct {
|
||||
Command string
|
||||
}
|
||||
|
|
@ -86,14 +67,14 @@ func Increment(amount int) JsCommand {
|
|||
return JsCommand{Command: fmt.Sprintf("this.innerText = parseInt(this.innerText) + %d", amount)}
|
||||
}
|
||||
|
||||
func SetInnerHtml(r Renderable) JsCommand {
|
||||
func SetInnerHtml(r Ren) JsCommand {
|
||||
// language=JavaScript
|
||||
return JsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", Render(r.Render()))}
|
||||
return JsCommand{Command: fmt.Sprintf("this.innerHTML = `%s`", r.Render())}
|
||||
}
|
||||
|
||||
func SetOuterHtml(r Renderable) JsCommand {
|
||||
func SetOuterHtml(r Ren) JsCommand {
|
||||
// language=JavaScript
|
||||
return JsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", Render(r.Render()))}
|
||||
return JsCommand{Command: fmt.Sprintf("this.outerHTML = `%s`", r.Render())}
|
||||
}
|
||||
|
||||
func AddAttribute(name, value string) JsCommand {
|
||||
|
|
|
|||
|
|
@ -2,151 +2,13 @@ package h
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const FlagSkip = "skip"
|
||||
const FlagText = "text"
|
||||
const FlagRaw = "raw"
|
||||
const FlagAttributeList = "x-attribute-list"
|
||||
const FlagChildrenList = "x-children-list"
|
||||
|
||||
type Builder struct {
|
||||
builder *strings.Builder
|
||||
root *Node
|
||||
}
|
||||
|
||||
func (page Builder) render() {
|
||||
page.renderNode(page.root)
|
||||
}
|
||||
|
||||
func insertAttribute(node *Node, name string, value string) {
|
||||
existing := node.attributes[name]
|
||||
if existing != "" {
|
||||
node.attributes[name] = existing + " " + value
|
||||
} else {
|
||||
node.attributes[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
func (page Builder) renderNode(node *Node) {
|
||||
if node.tag != "" {
|
||||
page.builder.WriteString(fmt.Sprintf("<%s", node.tag))
|
||||
index := 0
|
||||
|
||||
if node.attributes == nil {
|
||||
node.attributes = map[string]string{}
|
||||
}
|
||||
|
||||
flatChildren := make([]Renderable, 0)
|
||||
for _, child := range node.children {
|
||||
|
||||
if child == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
c := child.Render()
|
||||
|
||||
flatChildren = append(flatChildren, child)
|
||||
if c.tag == FlagChildrenList {
|
||||
for _, gc := range c.children {
|
||||
flatChildren = append(flatChildren, gc)
|
||||
}
|
||||
c.tag = FlagSkip
|
||||
}
|
||||
}
|
||||
|
||||
if len(flatChildren) > 0 {
|
||||
node.children = flatChildren
|
||||
}
|
||||
|
||||
for _, child := range node.children {
|
||||
|
||||
if child == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
c := child.Render()
|
||||
|
||||
if c.tag == "class" {
|
||||
insertAttribute(node, "class", c.value)
|
||||
c.tag = FlagSkip
|
||||
}
|
||||
|
||||
if c.tag == FlagAttributeList {
|
||||
for _, gc := range c.children {
|
||||
gcr := gc.Render()
|
||||
for key, value := range gcr.attributes {
|
||||
insertAttribute(node, key, value)
|
||||
}
|
||||
gcr.tag = FlagSkip
|
||||
}
|
||||
c.tag = FlagSkip
|
||||
}
|
||||
|
||||
if c.tag == "attribute" {
|
||||
for key, value := range c.attributes {
|
||||
insertAttribute(node, key, value)
|
||||
}
|
||||
c.tag = FlagSkip
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range node.attributes {
|
||||
if index == 0 {
|
||||
page.builder.WriteString(" ")
|
||||
}
|
||||
page.builder.WriteString(key)
|
||||
page.builder.WriteString("=")
|
||||
page.builder.WriteRune('"')
|
||||
page.builder.WriteString(value)
|
||||
page.builder.WriteRune('"')
|
||||
if index < len(node.attributes) {
|
||||
page.builder.WriteRune(' ')
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
page.builder.WriteString(">")
|
||||
if node.text != "" {
|
||||
page.builder.WriteString(node.text)
|
||||
}
|
||||
}
|
||||
for _, child := range node.children {
|
||||
|
||||
if child == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
c := child.Render()
|
||||
|
||||
if c.tag == FlagText {
|
||||
page.builder.WriteString(c.text)
|
||||
continue
|
||||
}
|
||||
if c.tag == FlagRaw {
|
||||
page.builder.WriteString(c.value)
|
||||
continue
|
||||
}
|
||||
if c.tag != FlagSkip {
|
||||
page.renderNode(c)
|
||||
}
|
||||
}
|
||||
if node.tag != "" {
|
||||
page.builder.WriteString(fmt.Sprintf("</%s>", node.tag))
|
||||
}
|
||||
}
|
||||
|
||||
func Render(node Renderable) string {
|
||||
func Render(node Ren) string {
|
||||
start := time.Now()
|
||||
builder := strings.Builder{}
|
||||
page := Builder{
|
||||
builder: &builder,
|
||||
root: node.Render(),
|
||||
}
|
||||
page.render()
|
||||
d := page.builder.String()
|
||||
html := node.Render()
|
||||
duration := time.Since(start)
|
||||
fmt.Printf("render took %d\n", duration.Microseconds())
|
||||
return d
|
||||
fmt.Printf("render took %d microseconds\n", duration.Microseconds())
|
||||
return html
|
||||
}
|
||||
|
|
|
|||
137
framework/h/renderer.go
Normal file
137
framework/h/renderer.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package h
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (node *Element) Render() string {
|
||||
builder := &strings.Builder{}
|
||||
|
||||
// some elements may not have a tag, such as a Fragment
|
||||
if node.tag != "" {
|
||||
builder.WriteString("<" + node.tag)
|
||||
builder.WriteString(" ")
|
||||
for name, value := range node.attributes {
|
||||
builder.WriteString(NewAttribute(name, value).Render())
|
||||
}
|
||||
}
|
||||
|
||||
// first pass, flatten the children
|
||||
flatChildren := make([]Ren, 0)
|
||||
for _, child := range node.children {
|
||||
switch child.(type) {
|
||||
case *ChildList:
|
||||
flatChildren = append(flatChildren, child.(*ChildList).Children...)
|
||||
default:
|
||||
flatChildren = append(flatChildren, child)
|
||||
}
|
||||
}
|
||||
|
||||
node.children = flatChildren
|
||||
|
||||
// second pass, render any attributes within the tag
|
||||
for _, child := range node.children {
|
||||
switch child.(type) {
|
||||
case *AttributeMap:
|
||||
builder.WriteString(child.(*AttributeMap).Render())
|
||||
case *LifeCycle:
|
||||
builder.WriteString(child.(*LifeCycle).Render())
|
||||
}
|
||||
}
|
||||
|
||||
// close the tag
|
||||
if node.tag != "" {
|
||||
builder.WriteString(">")
|
||||
}
|
||||
|
||||
// render the children elements that are not attributes
|
||||
for _, child := range node.children {
|
||||
switch child.(type) {
|
||||
case *AttributeMap:
|
||||
continue
|
||||
case *LifeCycle:
|
||||
continue
|
||||
default:
|
||||
builder.WriteString(child.Render())
|
||||
}
|
||||
}
|
||||
|
||||
if node.tag != "" {
|
||||
builder.WriteString("</" + node.tag + ">")
|
||||
}
|
||||
|
||||
str := builder.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func (a *AttributeR) Render() string {
|
||||
return fmt.Sprintf(`%s="%s"`, a.Name, a.Value)
|
||||
}
|
||||
|
||||
func (t *TextContent) Render() string {
|
||||
return t.Content
|
||||
}
|
||||
|
||||
func (r *RawContent) Render() string {
|
||||
return r.Content
|
||||
}
|
||||
|
||||
func (c *ChildList) Render() string {
|
||||
builder := &strings.Builder{}
|
||||
for _, child := range c.Children {
|
||||
builder.WriteString(child.Render())
|
||||
}
|
||||
str := builder.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func (m *AttributeMap) Render() string {
|
||||
builder := &strings.Builder{}
|
||||
m2 := m.ToMap()
|
||||
|
||||
for k, v := range m2 {
|
||||
builder.WriteString(NewAttribute(k, v).Render())
|
||||
}
|
||||
|
||||
str := builder.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func (l *LifeCycle) Render() string {
|
||||
m := make(map[string]string)
|
||||
|
||||
for event, commands := range l.handlers {
|
||||
m[event] = ""
|
||||
for _, command := range commands {
|
||||
m[event] += fmt.Sprintf("%s;", command.Command)
|
||||
}
|
||||
}
|
||||
|
||||
children := make([]Ren, 0)
|
||||
|
||||
for event, js := range m {
|
||||
children = append(children, Attribute(event, js))
|
||||
}
|
||||
|
||||
result := Children(children...).Render()
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *AttributeMap) ToMap() map[string]string {
|
||||
result := make(map[string]string)
|
||||
for k, v := range *m {
|
||||
switch v.(type) {
|
||||
case AttributeMap:
|
||||
m2 := v.(*AttributeMap).ToMap()
|
||||
for _, a := range m2 {
|
||||
result[k] = a
|
||||
}
|
||||
case string:
|
||||
result[k] = v.(string)
|
||||
default:
|
||||
result[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
@ -8,31 +8,18 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
id string
|
||||
type Element struct {
|
||||
tag string
|
||||
attributes map[string]string
|
||||
children []Renderable
|
||||
text string
|
||||
value string
|
||||
changed bool
|
||||
children []Ren
|
||||
}
|
||||
|
||||
func (node *Node) Render() *Node {
|
||||
return node
|
||||
}
|
||||
|
||||
func (node *Node) AppendChild(child Renderable) Renderable {
|
||||
func (node *Element) AppendChild(child Ren) Ren {
|
||||
node.children = append(node.children, child)
|
||||
return node
|
||||
}
|
||||
|
||||
func (node *Node) SetChanged(changed bool) Renderable {
|
||||
node.changed = changed
|
||||
return node
|
||||
}
|
||||
|
||||
func Data(data map[string]any) Renderable {
|
||||
func Data(data map[string]any) Ren {
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return Empty()
|
||||
|
|
@ -40,86 +27,89 @@ func Data(data map[string]any) Renderable {
|
|||
return Attribute("x-data", string(serialized))
|
||||
}
|
||||
|
||||
func ClassIf(condition bool, value string) Renderable {
|
||||
func ClassIf(condition bool, value string) Ren {
|
||||
if condition {
|
||||
return Class(value)
|
||||
}
|
||||
return Empty()
|
||||
}
|
||||
|
||||
func Class(value ...string) Renderable {
|
||||
return &Node{
|
||||
tag: "class",
|
||||
value: MergeClasses(value...),
|
||||
}
|
||||
func Class(value ...string) Ren {
|
||||
return Attribute("class", MergeClasses(value...))
|
||||
}
|
||||
|
||||
func ClassX(value string, m map[string]bool) Renderable {
|
||||
func ClassX(value string, m ClassMap) Ren {
|
||||
builder := strings.Builder{}
|
||||
builder.WriteString(value + " ")
|
||||
builder.WriteString(value)
|
||||
builder.WriteString(" ")
|
||||
for k, v := range m {
|
||||
if v {
|
||||
builder.WriteString(k + " ")
|
||||
builder.WriteString(k)
|
||||
builder.WriteString(" ")
|
||||
}
|
||||
}
|
||||
return Class(builder.String())
|
||||
}
|
||||
|
||||
func MergeClasses(classes ...string) string {
|
||||
builder := ""
|
||||
for _, s := range classes {
|
||||
builder += s + " "
|
||||
if len(classes) == 1 {
|
||||
return classes[0]
|
||||
}
|
||||
return builder
|
||||
builder := strings.Builder{}
|
||||
for _, s := range classes {
|
||||
builder.WriteString(s)
|
||||
builder.WriteString(" ")
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func Id(value string) Renderable {
|
||||
func Id(value string) Ren {
|
||||
if strings.HasPrefix(value, "#") {
|
||||
value = value[1:]
|
||||
}
|
||||
return Attribute("id", value)
|
||||
}
|
||||
|
||||
func Attributes(attrs map[string]string) Renderable {
|
||||
return &Node{
|
||||
tag: "attribute",
|
||||
attributes: attrs,
|
||||
}
|
||||
type AttributeMap map[string]any
|
||||
type ClassMap map[string]bool
|
||||
|
||||
func Attributes(attrs *AttributeMap) *AttributeMap {
|
||||
return attrs
|
||||
}
|
||||
|
||||
func Checked() Renderable {
|
||||
func Checked() Ren {
|
||||
return Attribute("checked", "true")
|
||||
}
|
||||
|
||||
func Boost() Renderable {
|
||||
func Boost() Ren {
|
||||
return Attribute("hx-boost", "true")
|
||||
}
|
||||
|
||||
func Attribute(key string, value string) Renderable {
|
||||
return Attributes(map[string]string{key: value})
|
||||
func Attribute(key string, value string) *AttributeMap {
|
||||
return Attributes(&AttributeMap{key: value})
|
||||
}
|
||||
|
||||
func TriggerChildren() Renderable {
|
||||
func TriggerChildren() Ren {
|
||||
return HxExtension("trigger-children")
|
||||
}
|
||||
|
||||
func HxExtension(value string) Renderable {
|
||||
func HxExtension(value string) Ren {
|
||||
return Attribute("hx-ext", value)
|
||||
}
|
||||
|
||||
func Disabled() Renderable {
|
||||
func Disabled() Ren {
|
||||
return Attribute("disabled", "")
|
||||
}
|
||||
|
||||
func Get(path string) Renderable {
|
||||
func Get(path string) Ren {
|
||||
return Attribute("hx-get", path)
|
||||
}
|
||||
|
||||
func GetPartial(partial func(ctx *RequestContext) *Partial) Renderable {
|
||||
func GetPartial(partial func(ctx *RequestContext) *Partial) Ren {
|
||||
return Get(GetPartialPath(partial))
|
||||
}
|
||||
|
||||
func GetPartialWithQs(partial func(ctx *RequestContext) *Partial, qs string) Renderable {
|
||||
func GetPartialWithQs(partial func(ctx *RequestContext) *Partial, qs string) Ren {
|
||||
return Get(GetPartialPathWithQs(partial, qs))
|
||||
}
|
||||
|
||||
|
|
@ -130,172 +120,174 @@ func CreateTriggers(triggers ...string) []string {
|
|||
type ReloadParams struct {
|
||||
Triggers []string
|
||||
Target string
|
||||
Children Renderable
|
||||
Children Ren
|
||||
}
|
||||
|
||||
func ViewOnLoad(partial func(ctx *RequestContext) *Partial) Renderable {
|
||||
func ViewOnLoad(partial func(ctx *RequestContext) *Partial) Ren {
|
||||
return View(partial, ReloadParams{
|
||||
Triggers: CreateTriggers("load"),
|
||||
})
|
||||
}
|
||||
|
||||
func View(partial func(ctx *RequestContext) *Partial, params ReloadParams) Renderable {
|
||||
return Div(Attributes(map[string]string{
|
||||
func View(partial func(ctx *RequestContext) *Partial, params ReloadParams) Ren {
|
||||
return Div(Attributes(&AttributeMap{
|
||||
"hx-get": GetPartialPath(partial),
|
||||
"hx-trigger": strings.Join(params.Triggers, ", "),
|
||||
"hx-target": params.Target,
|
||||
}), params.Children)
|
||||
}
|
||||
|
||||
func PartialWithTriggers(partial func(ctx *RequestContext) *Partial, triggers ...string) Renderable {
|
||||
return Div(Attributes(map[string]string{
|
||||
func PartialWithTriggers(partial func(ctx *RequestContext) *Partial, triggers ...string) Ren {
|
||||
return Div(Attributes(&AttributeMap{
|
||||
"hx-get": GetPartialPath(partial),
|
||||
"hx-trigger": strings.Join(triggers, ", "),
|
||||
}))
|
||||
}
|
||||
|
||||
func GetWithQs(path string, qs map[string]string) Renderable {
|
||||
func GetWithQs(path string, qs map[string]string) Ren {
|
||||
return Get(SetQueryParams(path, qs))
|
||||
}
|
||||
|
||||
func Post(url string) Renderable {
|
||||
func PostPartialOnTrigger(partial func(ctx *RequestContext) *Partial, triggers ...string) Ren {
|
||||
return PostOnTrigger(GetPartialPath(partial), strings.Join(triggers, ", "))
|
||||
}
|
||||
|
||||
func PostPartialWithQsOnTrigger(partial func(ctx *RequestContext) *Partial, qs string, trigger string) Ren {
|
||||
return PostOnTrigger(GetPartialPathWithQs(partial, qs), trigger)
|
||||
}
|
||||
|
||||
func Post(url string) Ren {
|
||||
return Attribute("hx-post", url)
|
||||
}
|
||||
|
||||
func PostOnClick(url string) Renderable {
|
||||
return AttributeList(Attribute("hx-post", url), Trigger("click"))
|
||||
func PostOnTrigger(url string, trigger string) Ren {
|
||||
return AttributeList(Attribute("hx-post", url), Trigger(trigger))
|
||||
}
|
||||
|
||||
func PostPartialOnClick(partial func(ctx *RequestContext) *Partial) Renderable {
|
||||
func PostOnClick(url string) Ren {
|
||||
return PostOnTrigger(url, "click")
|
||||
}
|
||||
|
||||
func PostPartialOnClick(partial func(ctx *RequestContext) *Partial) Ren {
|
||||
return PostOnClick(GetPartialPath(partial))
|
||||
}
|
||||
|
||||
func PostPartialOnClickQs(partial func(ctx *RequestContext) *Partial, qs string) Renderable {
|
||||
func PostPartialOnClickQs(partial func(ctx *RequestContext) *Partial, qs string) Ren {
|
||||
return PostOnClick(GetPartialPathWithQs(partial, qs))
|
||||
}
|
||||
|
||||
func Trigger(trigger string) Renderable {
|
||||
func Trigger(trigger string) *AttributeMap {
|
||||
return Attribute("hx-trigger", trigger)
|
||||
}
|
||||
|
||||
func TextF(format string, args ...interface{}) Renderable {
|
||||
func TextF(format string, args ...interface{}) Ren {
|
||||
return Text(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func Text(text string) Renderable {
|
||||
return &Node{
|
||||
tag: "text",
|
||||
text: text,
|
||||
}
|
||||
func Text(text string) *TextContent {
|
||||
return NewTextContent(text)
|
||||
}
|
||||
|
||||
func Pf(format string, args ...interface{}) Renderable {
|
||||
func Pf(format string, args ...interface{}) Ren {
|
||||
return P(Text(fmt.Sprintf(format, args...)))
|
||||
}
|
||||
|
||||
func Target(target string) Renderable {
|
||||
func Target(target string) Ren {
|
||||
return Attribute("hx-target", target)
|
||||
}
|
||||
|
||||
func Name(name string) Renderable {
|
||||
func Name(name string) Ren {
|
||||
return Attribute("name", name)
|
||||
}
|
||||
|
||||
func Confirm(message string) Renderable {
|
||||
func Confirm(message string) Ren {
|
||||
return Attribute("hx-confirm", message)
|
||||
}
|
||||
|
||||
func Href(path string) Renderable {
|
||||
func Href(path string) Ren {
|
||||
return Attribute("href", path)
|
||||
}
|
||||
|
||||
func Type(name string) Renderable {
|
||||
func Type(name string) Ren {
|
||||
return Attribute("type", name)
|
||||
}
|
||||
|
||||
func Placeholder(placeholder string) Renderable {
|
||||
func Placeholder(placeholder string) Ren {
|
||||
return Attribute("placeholder", placeholder)
|
||||
}
|
||||
|
||||
func OutOfBandSwap(selector string) Renderable {
|
||||
func OutOfBandSwap(selector string) Ren {
|
||||
return Attribute("hx-swap-oob",
|
||||
Ternary(selector == "", "true", selector))
|
||||
}
|
||||
|
||||
func Click(value string) Renderable {
|
||||
func Click(value string) Ren {
|
||||
return Attribute("onclick", value)
|
||||
}
|
||||
|
||||
func Tag(tag string, children ...Renderable) Renderable {
|
||||
return &Node{
|
||||
func Tag(tag string, children ...Ren) *Element {
|
||||
return &Element{
|
||||
tag: tag,
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
func Html(children ...Renderable) Renderable {
|
||||
func Html(children ...Ren) *Element {
|
||||
return Tag("html", children...)
|
||||
}
|
||||
|
||||
func Head(children ...Renderable) Renderable {
|
||||
func Head(children ...Ren) *Element {
|
||||
return Tag("head", children...)
|
||||
}
|
||||
|
||||
func Body(children ...Renderable) Renderable {
|
||||
func Body(children ...Ren) *Element {
|
||||
return Tag("body", children...)
|
||||
}
|
||||
|
||||
func Link(href string, rel string) Renderable {
|
||||
return &Node{
|
||||
tag: "link",
|
||||
attributes: map[string]string{
|
||||
"href": href,
|
||||
"rel": rel,
|
||||
},
|
||||
children: make([]Renderable, 0),
|
||||
func Link(href string, rel string) Ren {
|
||||
attributeMap := AttributeMap{
|
||||
"href": href,
|
||||
"rel": rel,
|
||||
}
|
||||
return &Element{
|
||||
tag: "link",
|
||||
attributes: attributeMap.ToMap(),
|
||||
children: make([]Ren, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func Script(url string) Renderable {
|
||||
return &Node{
|
||||
tag: "script",
|
||||
attributes: map[string]string{
|
||||
"src": url,
|
||||
"type": "module",
|
||||
},
|
||||
children: make([]Renderable, 0),
|
||||
func Script(url string) Ren {
|
||||
attributeMap := AttributeMap{
|
||||
"src": url,
|
||||
}
|
||||
return &Element{
|
||||
tag: "script",
|
||||
attributes: attributeMap.ToMap(),
|
||||
children: make([]Ren, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func Raw(text string) Renderable {
|
||||
return &Node{
|
||||
tag: "raw",
|
||||
children: make([]Renderable, 0),
|
||||
value: text,
|
||||
}
|
||||
func Raw(text string) *RawContent {
|
||||
return NewRawContent(text)
|
||||
}
|
||||
|
||||
func MultiLineQuotes(text string) string {
|
||||
return "`" + text + "`"
|
||||
}
|
||||
|
||||
func RawF(text string, args any) Renderable {
|
||||
return &Node{
|
||||
tag: "raw",
|
||||
children: make([]Renderable, 0),
|
||||
value: fmt.Sprintf(text, args),
|
||||
}
|
||||
func RawF(text string, args any) *RawContent {
|
||||
return Raw(fmt.Sprintf(text, args))
|
||||
}
|
||||
|
||||
func RawScript(text string) Renderable {
|
||||
func RawScript(text string) *RawContent {
|
||||
return Raw("<script>" + text + "</script>")
|
||||
}
|
||||
|
||||
func Pre(children ...Renderable) Renderable {
|
||||
func Pre(children ...Ren) *Element {
|
||||
return Tag("pre", children...)
|
||||
}
|
||||
|
||||
func Div(children ...Renderable) Renderable {
|
||||
func Div(children ...Ren) *Element {
|
||||
return Tag("div", children...)
|
||||
}
|
||||
|
||||
|
|
@ -345,24 +337,25 @@ func NewHeaders(headers ...string) *Headers {
|
|||
return &m
|
||||
}
|
||||
|
||||
func Checkbox(children ...Renderable) Renderable {
|
||||
func Checkbox(children ...Ren) Ren {
|
||||
return Input("checkbox", children...)
|
||||
}
|
||||
|
||||
func Input(inputType string, children ...Renderable) Renderable {
|
||||
return &Node{
|
||||
tag: "input",
|
||||
attributes: map[string]string{
|
||||
"type": inputType,
|
||||
},
|
||||
children: children,
|
||||
func Input(inputType string, children ...Ren) Ren {
|
||||
attributeMap := AttributeMap{
|
||||
"type": inputType,
|
||||
}
|
||||
return &Element{
|
||||
tag: "input",
|
||||
attributes: attributeMap.ToMap(),
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
func List[T any](items []T, mapper func(item T, index int) Renderable) Renderable {
|
||||
node := &Node{
|
||||
func List[T any](items []T, mapper func(item T, index int) *Element) *Element {
|
||||
node := &Element{
|
||||
tag: "",
|
||||
children: make([]Renderable, len(items)),
|
||||
children: make([]Ren, len(items)),
|
||||
}
|
||||
for index, value := range items {
|
||||
node.children[index] = mapper(value, index)
|
||||
|
|
@ -370,159 +363,138 @@ func List[T any](items []T, mapper func(item T, index int) Renderable) Renderabl
|
|||
return node
|
||||
}
|
||||
|
||||
func Fragment(children ...Renderable) Renderable {
|
||||
return &Node{
|
||||
func Fragment(children ...Ren) Ren {
|
||||
return &Element{
|
||||
tag: "",
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
func AttributeList(children ...Renderable) Renderable {
|
||||
return &Node{
|
||||
tag: FlagAttributeList,
|
||||
children: children,
|
||||
func AttributeList(children ...*AttributeMap) *AttributeMap {
|
||||
m := make(AttributeMap)
|
||||
for _, child := range children {
|
||||
for k, v := range *child {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return &m
|
||||
}
|
||||
|
||||
func AppendChildren(node *Node, children ...Renderable) Renderable {
|
||||
func AppendChildren(node *Element, children ...Ren) Ren {
|
||||
node.children = append(node.children, children...)
|
||||
return node
|
||||
|
||||
}
|
||||
|
||||
func Button(children ...Renderable) Renderable {
|
||||
func Button(children ...Ren) *Element {
|
||||
return Tag("button", children...)
|
||||
}
|
||||
|
||||
func Indicator(tag string) Renderable {
|
||||
func Indicator(tag string) Ren {
|
||||
return Attribute("hx-indicator", tag)
|
||||
}
|
||||
|
||||
func P(children ...Renderable) Renderable {
|
||||
func P(children ...Ren) *Element {
|
||||
return Tag("p", children...)
|
||||
}
|
||||
|
||||
func H1(children ...Renderable) Renderable {
|
||||
func H1(children ...Ren) *Element {
|
||||
return Tag("h1", children...)
|
||||
}
|
||||
|
||||
func H2(children ...Renderable) Renderable {
|
||||
func H2(children ...Ren) *Element {
|
||||
return Tag("h2", children...)
|
||||
}
|
||||
|
||||
func H3(children ...Renderable) Renderable {
|
||||
func H3(children ...Ren) *Element {
|
||||
return Tag("h3", children...)
|
||||
}
|
||||
|
||||
func H4(children ...Renderable) Renderable {
|
||||
func H4(children ...Ren) *Element {
|
||||
return Tag("h4", children...)
|
||||
}
|
||||
|
||||
func H5(children ...Renderable) Renderable {
|
||||
func H5(children ...Ren) *Element {
|
||||
return Tag("h5", children...)
|
||||
}
|
||||
|
||||
func H6(children ...Renderable) Renderable {
|
||||
func H6(children ...Ren) *Element {
|
||||
return Tag("h6", children...)
|
||||
}
|
||||
|
||||
func Img(children ...Renderable) Renderable {
|
||||
func Img(children ...Ren) *Element {
|
||||
return Tag("img", children...)
|
||||
}
|
||||
|
||||
func Src(src string) Renderable {
|
||||
func Src(src string) Ren {
|
||||
return Attribute("src", src)
|
||||
}
|
||||
|
||||
func Form(children ...Renderable) Renderable {
|
||||
func Form(children ...Ren) *Element {
|
||||
return Tag("form", children...)
|
||||
}
|
||||
|
||||
func A(children ...Renderable) Renderable {
|
||||
func A(children ...Ren) *Element {
|
||||
return Tag("a", children...)
|
||||
}
|
||||
|
||||
func Nav(children ...Renderable) Renderable {
|
||||
func Nav(children ...Ren) *Element {
|
||||
return Tag("nav", children...)
|
||||
}
|
||||
|
||||
func Empty() Renderable {
|
||||
return &Node{
|
||||
func Empty() *Element {
|
||||
return &Element{
|
||||
tag: "",
|
||||
}
|
||||
}
|
||||
|
||||
func BeforeRequestSetHtml(children ...Renderable) Renderable {
|
||||
func BeforeRequestSetHtml(children ...Ren) Ren {
|
||||
serialized := Render(Fragment(children...))
|
||||
return Attribute("hx-on::before-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`)
|
||||
}
|
||||
|
||||
func BeforeRequestSetAttribute(key string, value string) Renderable {
|
||||
func BeforeRequestSetAttribute(key string, value string) Ren {
|
||||
return Attribute("hx-on::before-request", `this.setAttribute('`+key+`', '`+value+`')`)
|
||||
}
|
||||
|
||||
func OnMutationErrorSetText(text string) Renderable {
|
||||
func OnMutationErrorSetText(text string) Ren {
|
||||
return Attribute("hx-on::mutation-error", `this.innerText = '`+text+`'`)
|
||||
}
|
||||
|
||||
func BeforeRequestSetText(text string) Renderable {
|
||||
func BeforeRequestSetText(text string) Ren {
|
||||
return Attribute("hx-on::before-request", `this.innerText = '`+text+`'`)
|
||||
}
|
||||
|
||||
func AfterRequestSetText(text string) Renderable {
|
||||
func AfterRequestSetText(text string) Ren {
|
||||
return Attribute("hx-on::after-request", `this.innerText = '`+text+`'`)
|
||||
}
|
||||
|
||||
func AfterRequestRemoveAttribute(key string, value string) Renderable {
|
||||
func AfterRequestRemoveAttribute(key string, value string) Ren {
|
||||
return Attribute("hx-on::after-request", `this.removeAttribute('`+key+`')`)
|
||||
}
|
||||
|
||||
func IfQueryParam(key string, node *Node) Renderable {
|
||||
func IfQueryParam(key string, node *Element) Ren {
|
||||
return Fragment(Attribute("hx-if-qp:"+key, "true"), node)
|
||||
}
|
||||
|
||||
func Hidden() Renderable {
|
||||
func Hidden() Ren {
|
||||
return Attribute("style", "display:none")
|
||||
}
|
||||
|
||||
func MatchQueryParam(defaultValue string, active string, m map[string]*Node) Renderable {
|
||||
|
||||
rendered := make(map[string]string)
|
||||
for s, node := range m {
|
||||
rendered[s] = Render(node)
|
||||
}
|
||||
|
||||
root := Tag("span",
|
||||
m[active],
|
||||
Trigger("url"),
|
||||
Attribute("hx-match-qp", "true"),
|
||||
Attribute("hx-match-qp-default", defaultValue),
|
||||
)
|
||||
|
||||
for s, node := range rendered {
|
||||
root = AppendChildren(root.Render(), Attribute("hx-match-qp-mapping:"+s, ``+html.EscapeString(node)+``))
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
func AfterRequestSetHtml(children ...Renderable) Renderable {
|
||||
func AfterRequestSetHtml(children ...Ren) Ren {
|
||||
serialized := Render(Fragment(children...))
|
||||
return Attribute("hx-on::after-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`)
|
||||
}
|
||||
|
||||
func Children(children ...Renderable) Renderable {
|
||||
return &Node{
|
||||
tag: FlagChildrenList,
|
||||
children: children,
|
||||
}
|
||||
func Children(children ...Ren) *ChildList {
|
||||
return NewChildList(children...)
|
||||
}
|
||||
|
||||
func Label(text string) Renderable {
|
||||
func Label(text string) *Element {
|
||||
return Tag("label", Text(text))
|
||||
}
|
||||
|
||||
func If(condition bool, node Renderable) Renderable {
|
||||
func If(condition bool, node Ren) Ren {
|
||||
if condition {
|
||||
return node
|
||||
} else {
|
||||
|
|
@ -530,7 +502,7 @@ func If(condition bool, node Renderable) Renderable {
|
|||
}
|
||||
}
|
||||
|
||||
func IfElse(condition bool, node Renderable, node2 Renderable) Renderable {
|
||||
func IfElse(condition bool, node Ren, node2 Ren) Ren {
|
||||
if condition {
|
||||
return node
|
||||
} else {
|
||||
|
|
@ -538,7 +510,7 @@ func IfElse(condition bool, node Renderable, node2 Renderable) Renderable {
|
|||
}
|
||||
}
|
||||
|
||||
func IfElseLazy(condition bool, cb1 func() Renderable, cb2 func() Renderable) Renderable {
|
||||
func IfElseLazy(condition bool, cb1 func() Ren, cb2 func() Ren) Ren {
|
||||
if condition {
|
||||
return cb1()
|
||||
} else {
|
||||
|
|
@ -550,7 +522,7 @@ func GetTriggerName(ctx *RequestContext) string {
|
|||
return ctx.Request().Header.Get("HX-Trigger-Name")
|
||||
}
|
||||
|
||||
func IfHtmxRequest(ctx *RequestContext, node Renderable) Renderable {
|
||||
func IfHtmxRequest(ctx *RequestContext, node Ren) Ren {
|
||||
if ctx.Get("HX-Request") != "" {
|
||||
return node
|
||||
}
|
||||
|
|
@ -559,36 +531,35 @@ func IfHtmxRequest(ctx *RequestContext, node Renderable) Renderable {
|
|||
|
||||
type SwapArg struct {
|
||||
Selector string
|
||||
Content *Node
|
||||
Content *Element
|
||||
}
|
||||
|
||||
func NewSwap(selector string, content *Node) SwapArg {
|
||||
func NewSwap(selector string, content *Element) SwapArg {
|
||||
return SwapArg{
|
||||
Selector: selector,
|
||||
Content: content,
|
||||
}
|
||||
}
|
||||
|
||||
func OobSwap(ctx *RequestContext, content Renderable) Renderable {
|
||||
func OobSwap(ctx *RequestContext, content *Element) Ren {
|
||||
return OobSwapWithSelector(ctx, "", content)
|
||||
}
|
||||
|
||||
func OobSwapWithSelector(ctx *RequestContext, selector string, content Renderable) Renderable {
|
||||
func OobSwapWithSelector(ctx *RequestContext, selector string, content *Element) Ren {
|
||||
if ctx == nil || ctx.Get("HX-Request") == "" {
|
||||
return Empty()
|
||||
}
|
||||
c := content.Render()
|
||||
return c.AppendChild(OutOfBandSwap(selector))
|
||||
return content.AppendChild(OutOfBandSwap(selector))
|
||||
}
|
||||
|
||||
func SwapMany(ctx *RequestContext, args ...SwapArg) Renderable {
|
||||
func SwapMany(ctx *RequestContext, args ...SwapArg) Ren {
|
||||
if ctx.Get("HX-Request") == "" {
|
||||
return Empty()
|
||||
}
|
||||
for _, arg := range args {
|
||||
arg.Content.AppendChild(OutOfBandSwap(arg.Selector))
|
||||
}
|
||||
return Fragment(Map(args, func(arg SwapArg) Renderable {
|
||||
return Fragment(Map(args, func(arg SwapArg) Ren {
|
||||
return arg.Content
|
||||
})...)
|
||||
}
|
||||
|
|
@ -596,12 +567,12 @@ func SwapMany(ctx *RequestContext, args ...SwapArg) Renderable {
|
|||
type OnRequestSwapArgs struct {
|
||||
Target string
|
||||
Get string
|
||||
Default *Node
|
||||
BeforeRequest *Node
|
||||
AfterRequest *Node
|
||||
Default *Element
|
||||
BeforeRequest *Element
|
||||
AfterRequest *Element
|
||||
}
|
||||
|
||||
func OnRequestSwap(args OnRequestSwapArgs) Renderable {
|
||||
func OnRequestSwap(args OnRequestSwapArgs) Ren {
|
||||
return Div(args.Default,
|
||||
BeforeRequestSetHtml(args.BeforeRequest),
|
||||
AfterRequestSetHtml(args.AfterRequest),
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"net/url"
|
||||
)
|
||||
|
||||
type Renderable interface {
|
||||
Render() *Node
|
||||
type Ren interface {
|
||||
Render() string
|
||||
}
|
||||
|
||||
func Ternary[T any](value bool, a T, b T) T {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func StoryList() h.Renderable {
|
||||
func StoryList() h.Ren {
|
||||
|
||||
posts, _ := database.GetOrSet[[]Post]("posts", func() []Post {
|
||||
p, _ := List()
|
||||
|
|
@ -21,13 +21,13 @@ func StoryList() h.Renderable {
|
|||
}
|
||||
|
||||
return h.Fragment(
|
||||
h.Div(h.List(*posts, func(item Post, index int) h.Renderable {
|
||||
h.Div(h.List(*posts, func(item Post, index int) h.Ren {
|
||||
return StoryCard(item)
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
func StoryCard(post Post) h.Renderable {
|
||||
func StoryCard(post Post) h.Ren {
|
||||
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.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func StoryFull(id string) h.Renderable {
|
||||
func StoryFull(id string) h.Ren {
|
||||
post, err := Get(id)
|
||||
if err != nil {
|
||||
return h.Pf(err.Error())
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"starter-template/partials/sheet"
|
||||
)
|
||||
|
||||
func RootPage(children ...h.Renderable) h.Renderable {
|
||||
func RootPage(children ...h.Ren) h.Ren {
|
||||
return h.Html(
|
||||
h.HxExtension("path-deps, response-targets, mutation-error"),
|
||||
h.Head(
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func IndexPage(c echo.Context) *h.Page {
|
|||
))
|
||||
}
|
||||
|
||||
func CodeExample() h.Renderable {
|
||||
func CodeExample() h.Ren {
|
||||
code, err := os.ReadFile("pages/assets/_example.go")
|
||||
scriptSrc, err := os.ReadFile("pages/assets/shiki.js")
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func ListPage(ctx echo.Context) *h.Page {
|
|||
))
|
||||
}
|
||||
|
||||
func list(ctx echo.Context) h.Renderable {
|
||||
func list(ctx echo.Context) h.Ren {
|
||||
return h.Fragment(
|
||||
h.ViewOnLoad(partials.NewsSheet),
|
||||
h.Div(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"github.com/maddalax/htmgo/framework/h"
|
||||
)
|
||||
|
||||
func OpenSheetButton(open bool, children ...h.Renderable) h.Renderable {
|
||||
func OpenSheetButton(open bool, children ...h.Ren) h.Ren {
|
||||
if open {
|
||||
return ui.PrimaryButton(ui.ButtonProps{
|
||||
Id: "open-sheet",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ type Link struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
func NavBar() h.Renderable {
|
||||
func NavBar() h.Ren {
|
||||
|
||||
links := []Link{
|
||||
{"Home", "/"},
|
||||
|
|
@ -18,7 +18,7 @@ func NavBar() h.Renderable {
|
|||
return h.Nav(h.Class("flex gap-4 items-center p-4 text-slate-600"),
|
||||
h.Boost(),
|
||||
h.Children(
|
||||
h.Map(links, func(link Link) h.Renderable {
|
||||
h.Map(links, func(link Link) h.Ren {
|
||||
return h.A(h.Text(link.Name), h.Href(link.Path), h.Class("cursor-pointer hover:text-blue-400"))
|
||||
})...,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -36,15 +36,15 @@ func NewsSheetOpenCount(ctx echo.Context) *h.Partial {
|
|||
)
|
||||
}
|
||||
|
||||
func SheetWrapper(children ...h.Renderable) h.Renderable {
|
||||
func SheetWrapper(children ...h.Ren) h.Ren {
|
||||
return h.Div(h.Id("sheet-partial"), h.Fragment(children...))
|
||||
}
|
||||
|
||||
func SheetClosed() h.Renderable {
|
||||
func SheetClosed() h.Ren {
|
||||
return h.Div()
|
||||
}
|
||||
|
||||
func SheetOpen() h.Renderable {
|
||||
func SheetOpen() h.Ren {
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ func AddPatientSheetPartial(ctx echo.Context) *h.Partial {
|
|||
)
|
||||
}
|
||||
|
||||
func AddPatientSheet(onClosePath string) h.Renderable {
|
||||
func AddPatientSheet(onClosePath string) h.Ren {
|
||||
return sheet.Opened(
|
||||
sheet.Props{
|
||||
OnClosePath: onClosePath,
|
||||
|
|
@ -81,7 +81,7 @@ func ValidateForm(ctx echo.Context) *h.Partial {
|
|||
return h.NewPartial(h.Fragment())
|
||||
}
|
||||
|
||||
func addPatientForm() h.Renderable {
|
||||
func addPatientForm() h.Ren {
|
||||
return h.Form(
|
||||
h.HxExtension("debug, trigger-children"),
|
||||
h.Attribute("hx-target-5*", "#submit-error"),
|
||||
|
|
@ -123,7 +123,7 @@ func addPatientForm() h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func Row(patient *patient.Patient, index int) h.Renderable {
|
||||
func Row(patient *patient.Patient, index int) h.Ren {
|
||||
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),
|
||||
|
|
@ -131,7 +131,7 @@ func Row(patient *patient.Patient, index int) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func AddPatientButton() h.Renderable {
|
||||
func AddPatientButton() h.Ren {
|
||||
return ui.Button(ui.ButtonProps{
|
||||
Id: "add-patient",
|
||||
Text: "Add Patient",
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import (
|
|||
|
||||
type Props struct {
|
||||
ClassName string
|
||||
Root h.Renderable
|
||||
Root h.Ren
|
||||
OnClosePath string
|
||||
}
|
||||
|
||||
var Id = "#active-modal"
|
||||
|
||||
func Opened(props Props) h.Renderable {
|
||||
func Opened(props Props) h.Ren {
|
||||
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")),
|
||||
|
|
@ -24,7 +24,7 @@ func Opened(props Props) h.Renderable {
|
|||
)))
|
||||
}
|
||||
|
||||
func Closed() h.Renderable {
|
||||
func Closed() h.Ren {
|
||||
return h.Div(h.Id(Id))
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ func Close(ctx echo.Context) *h.Partial {
|
|||
)
|
||||
}
|
||||
|
||||
func closeButton(props Props) h.Renderable {
|
||||
func closeButton(props Props) h.Ren {
|
||||
return h.Div(
|
||||
h.Class("absolute top-0 right-0 p-3"),
|
||||
h.Button(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func Extensions() string {
|
|||
return strings.Join(extensions, ", ")
|
||||
}
|
||||
|
||||
func RootPage(children ...h.Renderable) h.Renderable {
|
||||
func RootPage(children ...h.Ren) h.Ren {
|
||||
return h.Html(
|
||||
h.HxExtension(Extensions()),
|
||||
h.Head(
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func IndexPage(c echo.Context) *h.Page {
|
|||
))
|
||||
}
|
||||
|
||||
func Button() h.Renderable {
|
||||
func Button() h.Ren {
|
||||
return h.Button(h.Class("btn bg-green-500 p-4 rounded text-white"),
|
||||
h.Text("my button"),
|
||||
h.AfterRequest(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func Extensions() string {
|
|||
return strings.Join(extensions, ", ")
|
||||
}
|
||||
|
||||
func RootPage(children ...h.Renderable) h.Renderable {
|
||||
func RootPage(children ...h.Ren) h.Ren {
|
||||
return h.Html(
|
||||
h.HxExtension(Extensions()),
|
||||
h.Head(
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func IndexPage(c echo.Context) *h.Page {
|
|||
))
|
||||
}
|
||||
|
||||
func Button() h.Renderable {
|
||||
func Button() h.Ren {
|
||||
return h.Button(h.Class("btn bg-green-500 p-4 rounded text-white"),
|
||||
h.Text("my button"),
|
||||
h.AfterRequest(
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ func TaskListPage(ctx *h.RequestContext) *h.Page {
|
|||
h.Class("flex flex-col gap-6 p-4 items-center max-w-xl mx-auto pb-12"),
|
||||
title,
|
||||
task.Card(ctx),
|
||||
h.Children(
|
||||
h.Div(h.Text("Double-click to edit a todo")),
|
||||
),
|
||||
),
|
||||
),
|
||||
))
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func getActiveTab(ctx *h.RequestContext) Tab {
|
|||
return TabAll
|
||||
}
|
||||
|
||||
func Card(ctx *h.RequestContext) h.Renderable {
|
||||
func Card(ctx *h.RequestContext) *h.Element {
|
||||
service := tasks.NewService(ctx.ServiceLocator())
|
||||
list, _ := service.List()
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ func Card(ctx *h.RequestContext) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func CardBody(list []*ent.Task, tab Tab) h.Renderable {
|
||||
func CardBody(list []*ent.Task, tab Tab) *h.Element {
|
||||
return h.Div(
|
||||
h.Id("tasks-card-body"),
|
||||
Input(list),
|
||||
|
|
@ -43,7 +43,7 @@ func CardBody(list []*ent.Task, tab Tab) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func Input(list []*ent.Task) h.Renderable {
|
||||
func Input(list []*ent.Task) *h.Element {
|
||||
return h.Div(
|
||||
h.Id("task-card-input"),
|
||||
h.Class("border border-b-slate-100 relative"),
|
||||
|
|
@ -61,7 +61,7 @@ func Input(list []*ent.Task) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func CompleteAllIcon(list []*ent.Task) h.Renderable {
|
||||
func CompleteAllIcon(list []*ent.Task) *h.Element {
|
||||
notCompletedCount := len(h.Filter(list, func(item *ent.Task) bool {
|
||||
return item.CompletedAt == nil
|
||||
}))
|
||||
|
|
@ -74,7 +74,7 @@ func CompleteAllIcon(list []*ent.Task) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func Footer(list []*ent.Task, activeTab Tab) h.Renderable {
|
||||
func Footer(list []*ent.Task, activeTab Tab) *h.Element {
|
||||
|
||||
notCompletedCount := len(h.Filter(list, func(item *ent.Task) bool {
|
||||
return item.CompletedAt == nil
|
||||
|
|
@ -90,7 +90,7 @@ func Footer(list []*ent.Task, activeTab Tab) h.Renderable {
|
|||
),
|
||||
h.Div(
|
||||
h.Class("flex items-center gap-4"),
|
||||
h.List(tabs, func(tab Tab, index int) h.Renderable {
|
||||
h.List(tabs, func(tab Tab, index int) *h.Element {
|
||||
return h.P(
|
||||
h.PostOnClick(h.GetPartialPathWithQs(ChangeTab, "tab="+tab)),
|
||||
h.ClassX("cursor-pointer px-2 py-1 rounded", map[string]bool{
|
||||
|
|
@ -110,12 +110,12 @@ func Footer(list []*ent.Task, activeTab Tab) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func List(list []*ent.Task, tab Tab) h.Renderable {
|
||||
func List(list []*ent.Task, tab Tab) *h.Element {
|
||||
return h.Div(
|
||||
h.Id("task-card-list"),
|
||||
h.Class("bg-white w-full"),
|
||||
h.Div(
|
||||
h.List(list, func(item *ent.Task, index int) h.Renderable {
|
||||
h.List(list, func(item *ent.Task, index int) *h.Element {
|
||||
if tab == TabActive && item.CompletedAt != nil {
|
||||
return h.Empty()
|
||||
}
|
||||
|
|
@ -128,10 +128,10 @@ func List(list []*ent.Task, tab Tab) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func Task(task *ent.Task, editing bool) h.Renderable {
|
||||
func Task(task *ent.Task, editing bool) *h.Element {
|
||||
return h.Div(
|
||||
h.Id(fmt.Sprintf("task-%s", task.ID.String())),
|
||||
h.ClassX("h-[80px] max-h-[80px] max-w-2xl flex items-center p-4 gap-4 cursor-pointer", map[string]bool{
|
||||
h.ClassX("h-[80px] max-h-[80px] max-w-2xl flex items-center p-4 gap-4 cursor-pointer", h.ClassMap{
|
||||
"border border-b-slate-100": !editing,
|
||||
}),
|
||||
CompleteIcon(task),
|
||||
|
|
@ -147,13 +147,17 @@ func Task(task *ent.Task, editing bool) h.Renderable {
|
|||
),
|
||||
h.Input(
|
||||
"text",
|
||||
h.Post(h.GetPartialPath(UpdateName)),
|
||||
h.Trigger("blur, keyup[keyCode==13]"),
|
||||
h.Attribute("autocomplete", "off"),
|
||||
h.Attribute("autofocus", "true"),
|
||||
h.Attribute("name", "name"),
|
||||
h.Class("pl-1 h-full w-full text-xl outline-none outline-2 outline-rose-300"),
|
||||
h.Attribute("value", task.Name),
|
||||
h.PostPartialOnTrigger(UpdateName, h.TriggerBlur, h.TriggerKeyUpEnter),
|
||||
h.Attributes(&h.AttributeMap{
|
||||
"placeholder": "What needs to be done?",
|
||||
"autofocus": "true",
|
||||
"autocomplete": "off",
|
||||
"name": "name",
|
||||
"class": h.ClassX("", h.ClassMap{
|
||||
"pl-1 h-full w-full text-xl outline-none outline-2 outline-rose-300": true,
|
||||
}),
|
||||
"value": task.Name,
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -168,7 +172,7 @@ func Task(task *ent.Task, editing bool) h.Renderable {
|
|||
)
|
||||
}
|
||||
|
||||
func CompleteIcon(task *ent.Task) h.Renderable {
|
||||
func CompleteIcon(task *ent.Task) *h.Element {
|
||||
return h.Div(
|
||||
h.Trigger("click"),
|
||||
h.Post(h.GetPartialPathWithQs(ToggleCompleted, "id="+task.ID.String())),
|
||||
|
|
|
|||
Loading…
Reference in a new issue