cleaning up the api a bit
This commit is contained in:
parent
e34a4fe269
commit
c555da4dc9
22 changed files with 641 additions and 218 deletions
|
|
@ -22,6 +22,8 @@ func Build() {
|
|||
// },
|
||||
//)
|
||||
|
||||
process.RunOrExit("env GOOS=linux GOARCH=amd64 go build -o ./dist .")
|
||||
process.RunOrExit("env GOOS=linux GOARCH=amd64 go build -o ./dist/app-linux-amd64 .")
|
||||
process.RunOrExit("go build -o ./dist/app .")
|
||||
|
||||
process.RunOrExit("echo \"Build successful\"")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package ui
|
|||
|
||||
import (
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
"github.com/maddalax/htmgo/framework/hx"
|
||||
)
|
||||
|
||||
type InputProps struct {
|
||||
|
|
@ -16,8 +17,7 @@ type InputProps struct {
|
|||
|
||||
func Input(props InputProps) h.Ren {
|
||||
validation := h.If(props.ValidationPath != "", h.Children(
|
||||
h.Post(props.ValidationPath),
|
||||
h.Trigger("change"),
|
||||
h.Post(props.ValidationPath, hx.ChangeEvent),
|
||||
h.Attribute("hx-swap", "innerHTML transition:true"),
|
||||
h.Attribute("hx-target", "next div"),
|
||||
))
|
||||
|
|
|
|||
2
framework/assets/dist/htmgo.js
vendored
2
framework/assets/dist/htmgo.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -6,9 +6,24 @@ let lastVersion = "";
|
|||
htmx.defineExtension("livereload", {
|
||||
init: function () {
|
||||
const host = window.location.host;
|
||||
|
||||
let enabled = false
|
||||
for (const element of Array.from(htmx.findAll("[hx-ext]"))) {
|
||||
const value = element.getAttribute("hx-ext");
|
||||
if(value?.split(" ").includes("livereload")) {
|
||||
enabled = true
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('livereload extension initialized.');
|
||||
|
||||
createWebSocketClient({
|
||||
url: `ws://${host}/dev/livereload`,
|
||||
url: `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${host}/dev/livereload`,
|
||||
onOpen: () => {
|
||||
},
|
||||
onMessage: (message) => {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,6 @@ func GetPartialPath(partial func(ctx *RequestContext) *Partial) string {
|
|||
return runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name()
|
||||
}
|
||||
|
||||
func GetPartialPathWithQs(partial func(ctx *RequestContext) *Partial, qs string) string {
|
||||
return html.EscapeString(GetPartialPath(partial) + "?" + qs)
|
||||
func GetPartialPathWithQs(partial func(ctx *RequestContext) *Partial, qs *Qs) string {
|
||||
return html.EscapeString(GetPartialPath(partial) + "?" + qs.ToString())
|
||||
}
|
||||
|
|
|
|||
32
framework/h/conditionals.go
Normal file
32
framework/h/conditionals.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package h
|
||||
|
||||
func If(condition bool, node Ren) Ren {
|
||||
if condition {
|
||||
return node
|
||||
} else {
|
||||
return Empty()
|
||||
}
|
||||
}
|
||||
|
||||
func IfElse(condition bool, node Ren, node2 Ren) Ren {
|
||||
if condition {
|
||||
return node
|
||||
} else {
|
||||
return node2
|
||||
}
|
||||
}
|
||||
|
||||
func IfElseLazy(condition bool, cb1 func() Ren, cb2 func() Ren) Ren {
|
||||
if condition {
|
||||
return cb1()
|
||||
} else {
|
||||
return cb2()
|
||||
}
|
||||
}
|
||||
|
||||
func IfHtmxRequest(ctx *RequestContext, node Ren) Ren {
|
||||
if ctx.Get("HX-Request") != "" {
|
||||
return node
|
||||
}
|
||||
return Empty()
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package h
|
||||
|
||||
type HxEvent = string
|
||||
type HxTriggerName = string
|
||||
|
||||
var (
|
||||
HxBeforeRequest HxEvent = "hx-on::before-request"
|
||||
HxAfterRequest HxEvent = "hx-on::after-request"
|
||||
HxOnMutationError HxEvent = "hx-on::mutation-error"
|
||||
HxOnLoad HxEvent = "hx-on::load"
|
||||
HxOnLoadError HxEvent = "hx-on::load-error"
|
||||
HxRequestTimeout HxEvent = "hx-on::request-timeout"
|
||||
HxTrigger HxEvent = "hx-on::trigger"
|
||||
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"
|
||||
)
|
||||
|
|
@ -2,61 +2,104 @@ package h
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/maddalax/htmgo/framework/hx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LifeCycle struct {
|
||||
handlers map[HxEvent][]JsCommand
|
||||
handlers map[hx.Event][]Command
|
||||
}
|
||||
|
||||
func NewLifeCycle() *LifeCycle {
|
||||
return &LifeCycle{
|
||||
handlers: make(map[HxEvent][]JsCommand),
|
||||
handlers: make(map[hx.Event][]Command),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LifeCycle) OnEvent(event HxEvent, cmd ...JsCommand) *LifeCycle {
|
||||
if l.handlers[event] == nil {
|
||||
l.handlers[event] = []JsCommand{}
|
||||
func validateCommands(cmds []Command) {
|
||||
for _, cmd := range cmds {
|
||||
switch t := cmd.(type) {
|
||||
case JsCommand:
|
||||
break
|
||||
case *AttributeMap:
|
||||
break
|
||||
case *Element:
|
||||
panic(fmt.Sprintf("element is not allowed in lifecycle events. Got: %v", t))
|
||||
default:
|
||||
panic(fmt.Sprintf("type is not allowed in lifecycle events. Got: %v", t))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *LifeCycle) OnEvent(event hx.Event, cmd ...Command) *LifeCycle {
|
||||
validateCommands(cmd)
|
||||
|
||||
if l.handlers[event] == nil {
|
||||
l.handlers[event] = []Command{}
|
||||
}
|
||||
|
||||
l.handlers[event] = append(l.handlers[event], cmd...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LifeCycle) BeforeRequest(cmd ...JsCommand) *LifeCycle {
|
||||
l.OnEvent(HxBeforeRequest, cmd...)
|
||||
func (l *LifeCycle) BeforeRequest(cmd ...Command) *LifeCycle {
|
||||
l.OnEvent(hx.BeforeRequestEvent, cmd...)
|
||||
return l
|
||||
}
|
||||
|
||||
func OnEvent(event HxEvent, cmd ...JsCommand) *LifeCycle {
|
||||
func OnLoad(cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().OnEvent(hx.LoadEvent, cmd...)
|
||||
}
|
||||
|
||||
func OnAfterSwap(cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().OnEvent(hx.AfterSwapEvent, cmd...)
|
||||
}
|
||||
|
||||
func OnTrigger(trigger string, cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().OnEvent(hx.NewStringTrigger(trigger).ToString(), cmd...)
|
||||
}
|
||||
|
||||
func OnClick(cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().OnEvent(hx.ClickEvent, cmd...)
|
||||
}
|
||||
|
||||
func OnEvent(event hx.Event, cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().OnEvent(event, cmd...)
|
||||
}
|
||||
|
||||
func BeforeRequest(cmd ...JsCommand) *LifeCycle {
|
||||
func BeforeRequest(cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().BeforeRequest(cmd...)
|
||||
}
|
||||
|
||||
func AfterRequest(cmd ...JsCommand) *LifeCycle {
|
||||
func AfterRequest(cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().AfterRequest(cmd...)
|
||||
}
|
||||
|
||||
func OnMutationError(cmd ...JsCommand) *LifeCycle {
|
||||
func OnMutationError(cmd ...Command) *LifeCycle {
|
||||
return NewLifeCycle().OnMutationError(cmd...)
|
||||
}
|
||||
|
||||
func (l *LifeCycle) AfterRequest(cmd ...JsCommand) *LifeCycle {
|
||||
l.OnEvent(HxAfterRequest, cmd...)
|
||||
func (l *LifeCycle) AfterRequest(cmd ...Command) *LifeCycle {
|
||||
l.OnEvent(hx.AfterRequestEvent, cmd...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *LifeCycle) OnMutationError(cmd ...JsCommand) *LifeCycle {
|
||||
l.OnEvent(HxOnMutationError, cmd...)
|
||||
func (l *LifeCycle) OnMutationError(cmd ...Command) *LifeCycle {
|
||||
l.OnEvent(hx.OnMutationErrorEvent, cmd...)
|
||||
return l
|
||||
}
|
||||
|
||||
type Command = Ren
|
||||
|
||||
type JsCommand struct {
|
||||
Command string
|
||||
}
|
||||
|
||||
func (j JsCommand) Render(builder *strings.Builder) {
|
||||
builder.WriteString(j.Command)
|
||||
}
|
||||
|
||||
func SetText(text string) JsCommand {
|
||||
// language=JavaScript
|
||||
return JsCommand{Command: fmt.Sprintf("this.innerText = '%s'", text)}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package h
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/maddalax/htmgo/framework/hx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
|
@ -89,20 +90,61 @@ func (m *AttributeMap) Render(builder *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
func toHtmxTriggerName(event string) string {
|
||||
if strings.HasPrefix(event, "htmx:") {
|
||||
return event[5:]
|
||||
}
|
||||
if strings.HasPrefix(event, "on") {
|
||||
return event[2:]
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func formatEventName(event string, isDomEvent bool) string {
|
||||
raw := toHtmxTriggerName(event)
|
||||
if isDomEvent {
|
||||
return "on" + raw
|
||||
}
|
||||
return event
|
||||
}
|
||||
|
||||
func (l *LifeCycle) fromAttributeMap(event string, key string, value string, builder *strings.Builder) {
|
||||
|
||||
if key == hx.GetAttr || key == hx.PatchAttr || key == hx.PostAttr {
|
||||
TriggerString(toHtmxTriggerName(event)).Render(builder)
|
||||
}
|
||||
|
||||
Attribute(key, value).Render(builder)
|
||||
}
|
||||
|
||||
func (l *LifeCycle) Render(builder *strings.Builder) {
|
||||
m := make(map[string]string)
|
||||
|
||||
for event, commands := range l.handlers {
|
||||
m[event] = ""
|
||||
for _, command := range commands {
|
||||
m[event] += fmt.Sprintf("%s;", command.Command)
|
||||
switch c := command.(type) {
|
||||
case JsCommand:
|
||||
eventName := formatEventName(event, true)
|
||||
m[eventName] += fmt.Sprintf("%s;", c.Command)
|
||||
case *AttributeMap:
|
||||
for k, v := range c.ToMap() {
|
||||
l.fromAttributeMap(event, k, v, builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
children := make([]Ren, 0)
|
||||
|
||||
for event, js := range m {
|
||||
children = append(children, Attribute(event, js))
|
||||
for event, value := range m {
|
||||
if value != "" {
|
||||
children = append(children, Attribute(event, value))
|
||||
}
|
||||
}
|
||||
|
||||
if len(children) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
Children(children...).Render(builder)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,51 @@ package h
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
"github.com/maddalax/htmgo/framework/hx"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Qs struct {
|
||||
m map[string]string
|
||||
}
|
||||
|
||||
func NewQs(pairs ...string) *Qs {
|
||||
q := &Qs{
|
||||
m: make(map[string]string),
|
||||
}
|
||||
if len(pairs)%2 != 0 {
|
||||
return q
|
||||
}
|
||||
for i := 0; i < len(pairs); i++ {
|
||||
q.m[pairs[i]] = pairs[i+1]
|
||||
i++
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *Qs) Add(key string, value string) *Qs {
|
||||
q.m[key] = value
|
||||
return q
|
||||
}
|
||||
|
||||
func (q *Qs) ToString() string {
|
||||
builder := strings.Builder{}
|
||||
index := 0
|
||||
for k, v := range q.m {
|
||||
builder.WriteString(k)
|
||||
builder.WriteString("=")
|
||||
builder.WriteString(v)
|
||||
if index < len(q.m)-1 {
|
||||
builder.WriteString("&")
|
||||
}
|
||||
index++
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
type PartialFunc = func(ctx *RequestContext) *Partial
|
||||
|
||||
type Element struct {
|
||||
tag string
|
||||
attributes map[string]string
|
||||
|
|
@ -76,12 +116,24 @@ func Attributes(attrs *AttributeMap) *AttributeMap {
|
|||
return attrs
|
||||
}
|
||||
|
||||
func AttributePairs(pairs ...string) *AttributeMap {
|
||||
if len(pairs)%2 != 0 {
|
||||
return &AttributeMap{}
|
||||
}
|
||||
m := make(AttributeMap)
|
||||
for i := 0; i < len(pairs); i++ {
|
||||
m[pairs[i]] = pairs[i+1]
|
||||
i++
|
||||
}
|
||||
return &m
|
||||
}
|
||||
|
||||
func Checked() Ren {
|
||||
return Attribute("checked", "true")
|
||||
}
|
||||
|
||||
func Boost() Ren {
|
||||
return Attribute("hx-boost", "true")
|
||||
return Attribute(hx.BoostAttr, "true")
|
||||
}
|
||||
|
||||
func Attribute(key string, value string) *AttributeMap {
|
||||
|
|
@ -93,90 +145,24 @@ func TriggerChildren() Ren {
|
|||
}
|
||||
|
||||
func HxExtension(value string) Ren {
|
||||
return Attribute("hx-ext", value)
|
||||
return Attribute(hx.ExtAttr, value)
|
||||
}
|
||||
|
||||
func Disabled() Ren {
|
||||
return Attribute("disabled", "")
|
||||
}
|
||||
|
||||
func Get(path string) Ren {
|
||||
return Attribute("hx-get", path)
|
||||
func TriggerString(triggers ...string) *AttributeMap {
|
||||
trigger := hx.NewStringTrigger(strings.Join(triggers, ", "))
|
||||
return Attribute(hx.TriggerAttr, trigger.ToString())
|
||||
}
|
||||
|
||||
func GetPartial(partial func(ctx *RequestContext) *Partial) Ren {
|
||||
return Get(GetPartialPath(partial))
|
||||
func Trigger(opts ...hx.TriggerEvent) *AttributeMap {
|
||||
return Attribute(hx.TriggerAttr, hx.NewTrigger(opts...).ToString())
|
||||
}
|
||||
|
||||
func GetPartialWithQs(partial func(ctx *RequestContext) *Partial, qs string) Ren {
|
||||
return Get(GetPartialPathWithQs(partial, qs))
|
||||
}
|
||||
|
||||
func CreateTriggers(triggers ...string) []string {
|
||||
return triggers
|
||||
}
|
||||
|
||||
type ReloadParams struct {
|
||||
Triggers []string
|
||||
Target string
|
||||
Children Ren
|
||||
}
|
||||
|
||||
func ViewOnLoad(partial func(ctx *RequestContext) *Partial) Ren {
|
||||
return View(partial, ReloadParams{
|
||||
Triggers: CreateTriggers("load"),
|
||||
})
|
||||
}
|
||||
|
||||
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) Ren {
|
||||
return Div(Attributes(&AttributeMap{
|
||||
"hx-get": GetPartialPath(partial),
|
||||
"hx-trigger": strings.Join(triggers, ", "),
|
||||
}))
|
||||
}
|
||||
|
||||
func GetWithQs(path string, qs map[string]string) Ren {
|
||||
return Get(SetQueryParams(path, qs))
|
||||
}
|
||||
|
||||
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 PostOnTrigger(url string, trigger string) Ren {
|
||||
return AttributeList(Attribute("hx-post", url), Trigger(trigger))
|
||||
}
|
||||
|
||||
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) Ren {
|
||||
return PostOnClick(GetPartialPathWithQs(partial, qs))
|
||||
}
|
||||
|
||||
func Trigger(trigger string) *AttributeMap {
|
||||
return Attribute("hx-trigger", trigger)
|
||||
func TriggerClick(opts ...hx.Modifier) *AttributeMap {
|
||||
return Trigger(hx.OnClick(opts...))
|
||||
}
|
||||
|
||||
func TextF(format string, args ...interface{}) Ren {
|
||||
|
|
@ -192,7 +178,7 @@ func Pf(format string, args ...interface{}) Ren {
|
|||
}
|
||||
|
||||
func Target(target string) Ren {
|
||||
return Attribute("hx-target", target)
|
||||
return Attribute(hx.TargetAttr, target)
|
||||
}
|
||||
|
||||
func Name(name string) Ren {
|
||||
|
|
@ -200,7 +186,7 @@ func Name(name string) Ren {
|
|||
}
|
||||
|
||||
func Confirm(message string) Ren {
|
||||
return Attribute("hx-confirm", message)
|
||||
return Attribute(hx.ConfirmAttr, message)
|
||||
}
|
||||
|
||||
func Href(path string) Ren {
|
||||
|
|
@ -216,7 +202,7 @@ func Placeholder(placeholder string) Ren {
|
|||
}
|
||||
|
||||
func OutOfBandSwap(selector string) Ren {
|
||||
return Attribute("hx-swap-oob",
|
||||
return Attribute(hx.SwapOobAttr,
|
||||
Ternary(selector == "", "true", selector))
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +293,7 @@ func Article(children ...Ren) *Element {
|
|||
}
|
||||
|
||||
func ReplaceUrlHeader(url string) *Headers {
|
||||
return NewHeaders("HX-Replace-Url", url)
|
||||
return NewHeaders(hx.ReplaceUrlHeader, url)
|
||||
}
|
||||
|
||||
func CombineHeaders(headers ...*Headers) *Headers {
|
||||
|
|
@ -321,7 +307,7 @@ func CombineHeaders(headers ...*Headers) *Headers {
|
|||
}
|
||||
|
||||
func CurrentPath(ctx *RequestContext) string {
|
||||
current := ctx.Request().Header.Get("Hx-Current-Url")
|
||||
current := ctx.Request().Header.Get(hx.CurrentUrlHeader)
|
||||
parsed, err := url.Parse(current)
|
||||
if err != nil {
|
||||
return ""
|
||||
|
|
@ -330,12 +316,12 @@ func CurrentPath(ctx *RequestContext) string {
|
|||
}
|
||||
|
||||
func PushQsHeader(ctx *RequestContext, key string, value string) *Headers {
|
||||
current := ctx.Request().Header.Get("Hx-Current-Url")
|
||||
current := ctx.Request().Header.Get(hx.CurrentUrlHeader)
|
||||
parsed, err := url.Parse(current)
|
||||
if err != nil {
|
||||
return NewHeaders()
|
||||
}
|
||||
return NewHeaders("HX-Replace-Url", SetQueryParams(parsed.Path, map[string]string{
|
||||
return NewHeaders(hx.ReplaceUrlHeader, SetQueryParams(parsed.Path, map[string]string{
|
||||
key: value,
|
||||
}))
|
||||
}
|
||||
|
|
@ -402,8 +388,8 @@ func Button(children ...Ren) *Element {
|
|||
return Tag("button", children...)
|
||||
}
|
||||
|
||||
func Indicator(tag string) Ren {
|
||||
return Attribute("hx-indicator", tag)
|
||||
func Indicator(tag string) *AttributeMap {
|
||||
return Attribute(hx.IndicatorAttr, tag)
|
||||
}
|
||||
|
||||
func P(children ...Ren) *Element {
|
||||
|
|
@ -460,31 +446,6 @@ func Empty() *Element {
|
|||
}
|
||||
}
|
||||
|
||||
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) Ren {
|
||||
return Attribute("hx-on::before-request", `this.setAttribute('`+key+`', '`+value+`')`)
|
||||
}
|
||||
|
||||
func OnMutationErrorSetText(text string) Ren {
|
||||
return Attribute("hx-on::mutation-error", `this.innerText = '`+text+`'`)
|
||||
}
|
||||
|
||||
func BeforeRequestSetText(text string) Ren {
|
||||
return Attribute("hx-on::before-request", `this.innerText = '`+text+`'`)
|
||||
}
|
||||
|
||||
func AfterRequestSetText(text string) Ren {
|
||||
return Attribute("hx-on::after-request", `this.innerText = '`+text+`'`)
|
||||
}
|
||||
|
||||
func AfterRequestRemoveAttribute(key string, value string) Ren {
|
||||
return Attribute("hx-on::after-request", `this.removeAttribute('`+key+`')`)
|
||||
}
|
||||
|
||||
func IfQueryParam(key string, node *Element) Ren {
|
||||
return Fragment(Attribute("hx-if-qp:"+key, "true"), node)
|
||||
}
|
||||
|
|
@ -493,11 +454,6 @@ func Hidden() Ren {
|
|||
return Attribute("style", "display:none")
|
||||
}
|
||||
|
||||
func AfterRequestSetHtml(children ...Ren) Ren {
|
||||
serialized := Render(Fragment(children...))
|
||||
return Attribute("hx-on::after-request", `this.innerHTML = '`+html.EscapeString(serialized)+`'`)
|
||||
}
|
||||
|
||||
func Children(children ...Ren) *ChildList {
|
||||
return NewChildList(children...)
|
||||
}
|
||||
|
|
@ -506,41 +462,10 @@ func Label(text string) *Element {
|
|||
return Tag("label", Text(text))
|
||||
}
|
||||
|
||||
func If(condition bool, node Ren) Ren {
|
||||
if condition {
|
||||
return node
|
||||
} else {
|
||||
return Empty()
|
||||
}
|
||||
}
|
||||
|
||||
func IfElse(condition bool, node Ren, node2 Ren) Ren {
|
||||
if condition {
|
||||
return node
|
||||
} else {
|
||||
return node2
|
||||
}
|
||||
}
|
||||
|
||||
func IfElseLazy(condition bool, cb1 func() Ren, cb2 func() Ren) Ren {
|
||||
if condition {
|
||||
return cb1()
|
||||
} else {
|
||||
return cb2()
|
||||
}
|
||||
}
|
||||
|
||||
func GetTriggerName(ctx *RequestContext) string {
|
||||
return ctx.Request().Header.Get("HX-Trigger-Name")
|
||||
}
|
||||
|
||||
func IfHtmxRequest(ctx *RequestContext, node Ren) Ren {
|
||||
if ctx.Get("HX-Request") != "" {
|
||||
return node
|
||||
}
|
||||
return Empty()
|
||||
}
|
||||
|
||||
type SwapArg struct {
|
||||
Selector string
|
||||
Content *Element
|
||||
|
|
@ -575,20 +500,3 @@ func SwapMany(ctx *RequestContext, args ...SwapArg) Ren {
|
|||
return arg.Content
|
||||
})...)
|
||||
}
|
||||
|
||||
type OnRequestSwapArgs struct {
|
||||
Target string
|
||||
Get string
|
||||
Default *Element
|
||||
BeforeRequest *Element
|
||||
AfterRequest *Element
|
||||
}
|
||||
|
||||
func OnRequestSwap(args OnRequestSwapArgs) Ren {
|
||||
return Div(args.Default,
|
||||
BeforeRequestSetHtml(args.BeforeRequest),
|
||||
AfterRequestSetHtml(args.AfterRequest),
|
||||
Get(args.Get),
|
||||
Target(args.Target),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
43
framework/h/xhr.go
Normal file
43
framework/h/xhr.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package h
|
||||
|
||||
import "github.com/maddalax/htmgo/framework/hx"
|
||||
|
||||
func Get(path string, trigger ...string) *AttributeMap {
|
||||
return AttributeList(Attribute(hx.GetAttr, path), TriggerString(trigger...))
|
||||
}
|
||||
|
||||
func GetPartial(partial PartialFunc, trigger ...string) *AttributeMap {
|
||||
return Get(GetPartialPath(partial), trigger...)
|
||||
}
|
||||
|
||||
func GetPartialWithQs(partial PartialFunc, qs *Qs, trigger string) *AttributeMap {
|
||||
return Get(GetPartialPathWithQs(partial, qs), trigger)
|
||||
}
|
||||
|
||||
func GetWithQs(path string, qs map[string]string, trigger string) *AttributeMap {
|
||||
return Get(SetQueryParams(path, qs), trigger)
|
||||
}
|
||||
|
||||
func PostPartial(partial PartialFunc, triggers ...string) *AttributeMap {
|
||||
return Post(GetPartialPath(partial), triggers...)
|
||||
}
|
||||
|
||||
func PostPartialWithQs(partial PartialFunc, qs *Qs, trigger ...string) *AttributeMap {
|
||||
return Post(GetPartialPathWithQs(partial, qs), trigger...)
|
||||
}
|
||||
|
||||
func Post(url string, trigger ...string) *AttributeMap {
|
||||
return AttributeList(Attribute(hx.PostAttr, url), TriggerString(trigger...))
|
||||
}
|
||||
|
||||
func PostOnClick(url string) *AttributeMap {
|
||||
return Post(url, hx.ClickEvent)
|
||||
}
|
||||
|
||||
func PostPartialOnClick(partial PartialFunc) *AttributeMap {
|
||||
return PostOnClick(GetPartialPath(partial))
|
||||
}
|
||||
|
||||
func PostPartialOnClickQs(partial PartialFunc, qs *Qs) *AttributeMap {
|
||||
return PostOnClick(GetPartialPathWithQs(partial, qs))
|
||||
}
|
||||
26
framework/hx/event.go
Normal file
26
framework/hx/event.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package hx
|
||||
|
||||
import "fmt"
|
||||
|
||||
func OnEvent(event Event, modifiers ...Modifier) TriggerEvent {
|
||||
return TriggerEvent{
|
||||
event: event,
|
||||
modifiers: modifiers,
|
||||
}
|
||||
}
|
||||
|
||||
func OnClick(modifiers ...Modifier) TriggerEvent {
|
||||
return OnEvent(ClickEvent, modifiers...)
|
||||
}
|
||||
|
||||
func OnLoad(modifiers ...Modifier) TriggerEvent {
|
||||
return OnEvent(LoadEvent, modifiers...)
|
||||
}
|
||||
|
||||
func OnChange(modifiers ...Modifier) TriggerEvent {
|
||||
return OnEvent(ChangeEvent, modifiers...)
|
||||
}
|
||||
|
||||
func OnPoll(durationSeconds int) TriggerEvent {
|
||||
return OnEvent(PollingEvent, StringModifier(fmt.Sprintf("%ds", durationSeconds)))
|
||||
}
|
||||
138
framework/hx/htmx.go
Normal file
138
framework/hx/htmx.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package hx
|
||||
|
||||
type Attribute = string
|
||||
type Header = string
|
||||
type Event = string
|
||||
|
||||
// https://htmx.org/reference/#events
|
||||
const (
|
||||
GetAttr Attribute = "hx-get"
|
||||
PostAttr Attribute = "hx-post"
|
||||
PushUrlAttr Attribute = "hx-push-url"
|
||||
SelectAttr Attribute = "hx-select"
|
||||
SelectOobAttr Attribute = "hx-select-oob"
|
||||
SwapAttr Attribute = "hx-swap"
|
||||
SwapOobAttr Attribute = "hx-swap-oob"
|
||||
TargetAttr Attribute = "hx-target"
|
||||
TriggerAttr Attribute = "hx-trigger"
|
||||
ValsAttr Attribute = "hx-vals"
|
||||
BoostAttr Attribute = "hx-boost"
|
||||
ConfirmAttr Attribute = "hx-confirm"
|
||||
DeleteAttr Attribute = "hx-delete"
|
||||
DisableAttr Attribute = "hx-disable"
|
||||
DisabledEltAttr Attribute = "hx-disabled-elt"
|
||||
DisinheritAttr Attribute = "hx-disinherit"
|
||||
EncodingAttr Attribute = "hx-encoding"
|
||||
ExtAttr Attribute = "hx-ext"
|
||||
HeadersAttr Attribute = "hx-headers"
|
||||
HistoryAttr Attribute = "hx-history"
|
||||
HistoryEltAttr Attribute = "hx-history-elt"
|
||||
IncludeAttr Attribute = "hx-include"
|
||||
IndicatorAttr Attribute = "hx-indicator"
|
||||
InheritAttr Attribute = "hx-inherit"
|
||||
ParamsAttr Attribute = "hx-params"
|
||||
PatchAttr Attribute = "hx-patch"
|
||||
PreserveAttr Attribute = "hx-preserve"
|
||||
PromptAttr Attribute = "hx-prompt"
|
||||
PutAttr Attribute = "hx-put"
|
||||
ReplaceUrlAttr Attribute = "hx-replace-url"
|
||||
RequestAttr Attribute = "hx-request"
|
||||
SyncAttr Attribute = "hx-sync"
|
||||
ValidateAttr Attribute = "hx-validate"
|
||||
)
|
||||
|
||||
const (
|
||||
LocationHeader Header = "HX-Location"
|
||||
PushUrlHeader Header = "HX-Push-Url"
|
||||
RedirectHeader Header = "HX-Redirect"
|
||||
RefreshHeader Header = "HX-Refresh"
|
||||
ReplaceUrlHeader Header = "HX-Replace-Url"
|
||||
CurrentUrlHeader Header = "HX-Current-Url"
|
||||
ReswapHeader Header = "HX-Reswap"
|
||||
RetargetHeader Header = "HX-Retarget"
|
||||
ReselectHeader Header = "HX-Reselect"
|
||||
TriggerHeader Header = "HX-Trigger"
|
||||
TriggerAfterSettleHeader Header = "HX-Trigger-After-Settle"
|
||||
TriggerAfterSwapHeader Header = "HX-Trigger-After-Swap"
|
||||
)
|
||||
|
||||
const (
|
||||
// AbortEvent Htmx Events
|
||||
AbortEvent Event = "htmx:abort"
|
||||
AfterOnLoadEvent Event = "htmx:afterOnLoad"
|
||||
AfterProcessNodeEvent Event = "htmx:afterProcessNode"
|
||||
AfterRequestEvent Event = "htmx:afterRequest"
|
||||
OnMutationErrorEvent Event = "htmx:onMutationError"
|
||||
AfterSettleEvent Event = "htmx:afterSettle"
|
||||
AfterSwapEvent Event = "htmx:afterSwap"
|
||||
BeforeCleanupElementEvent Event = "htmx:beforeCleanupElement"
|
||||
BeforeOnLoadEvent Event = "htmx:beforeOnLoad"
|
||||
BeforeProcessNodeEvent Event = "htmx:beforeProcessNode"
|
||||
BeforeRequestEvent Event = "htmx:beforeRequest"
|
||||
BeforeSwapEvent Event = "htmx:beforeSwap"
|
||||
BeforeSendEvent Event = "htmx:beforeSend"
|
||||
ConfigRequestEvent Event = "htmx:configRequest"
|
||||
ConfirmEvent Event = "htmx:confirm"
|
||||
HistoryCacheErrorEvent Event = "htmx:historyCacheError"
|
||||
HistoryCacheMissEvent Event = "htmx:historyCacheMiss"
|
||||
HistoryCacheMissErrorEvent Event = "htmx:historyCacheMissError"
|
||||
HistoryCacheMissLoadEvent Event = "htmx:historyCacheMissLoad"
|
||||
HistoryRestoreEvent Event = "htmx:historyRestore"
|
||||
BeforeHistorySaveEvent Event = "htmx:beforeHistorySave"
|
||||
LoadEvent Event = "htmx:load"
|
||||
NoSSESourceErrorEvent Event = "htmx:noSSESourceError"
|
||||
OnLoadErrorEvent Event = "htmx:onLoadError"
|
||||
OobAfterSwapEvent Event = "htmx:oobAfterSwap"
|
||||
OobBeforeSwapEvent Event = "htmx:oobBeforeSwap"
|
||||
OobErrorNoTargetEvent Event = "htmx:oobErrorNoTarget"
|
||||
PromptEvent Event = "htmx:prompt"
|
||||
PushedIntoHistoryEvent Event = "htmx:pushedIntoHistory"
|
||||
ResponseErrorEvent Event = "htmx:responseError"
|
||||
SendErrorEvent Event = "htmx:sendError"
|
||||
SSEErrorEvent Event = "htmx:sseError"
|
||||
SSEOpenEvent Event = "htmx:sseOpen"
|
||||
SwapErrorEvent Event = "htmx:swapError"
|
||||
TargetErrorEvent Event = "htmx:targetError"
|
||||
TimeoutEvent Event = "htmx:timeout"
|
||||
ValidationValidateEvent Event = "htmx:validation:validate"
|
||||
ValidationFailedEvent Event = "htmx:validation:failed"
|
||||
ValidationHaltedEvent Event = "htmx:validation:halted"
|
||||
XhrAbortEvent Event = "htmx:xhr:abort"
|
||||
XhrLoadEndEvent Event = "htmx:xhr:loadend"
|
||||
XhrLoadStartEvent Event = "htmx:xhr:loadstart"
|
||||
XhrProgressEvent Event = "htmx:xhr:progress"
|
||||
|
||||
// RevealedEvent Misc Events
|
||||
RevealedEvent Event = "revealed"
|
||||
InstersectEvent Event = "intersect"
|
||||
PollingEvent Event = "every"
|
||||
|
||||
// ClickEvent Dom Events
|
||||
ClickEvent Event = "onclick"
|
||||
ChangeEvent Event = "onchange"
|
||||
InputEvent Event = "oninput"
|
||||
FocusEvent Event = "onfocus"
|
||||
BlurEvent Event = "onblur"
|
||||
KeyDownEvent Event = "onkeydown"
|
||||
KeyUpEvent Event = "onkeyup"
|
||||
KeyPressEvent Event = "onkeypress"
|
||||
SubmitEvent Event = "onsubmit"
|
||||
LoadDomEvent Event = "onload"
|
||||
UnloadEvent Event = "onunload"
|
||||
ResizeEvent Event = "onresize"
|
||||
ScrollEvent Event = "onscroll"
|
||||
DblClickEvent Event = "ondblclick"
|
||||
MouseOverEvent Event = "onmouseover"
|
||||
MouseOutEvent Event = "onmouseout"
|
||||
MouseMoveEvent Event = "onmousemove"
|
||||
MouseDownEvent Event = "onmousedown"
|
||||
MouseUpEvent Event = "onmouseup"
|
||||
ContextMenuEvent Event = "oncontextmenu"
|
||||
DragStartEvent Event = "ondragstart"
|
||||
DragEvent Event = "ondrag"
|
||||
DragEnterEvent Event = "ondragenter"
|
||||
DragLeaveEvent Event = "ondragleave"
|
||||
DragOverEvent Event = "ondragover"
|
||||
DropEvent Event = "ondrop"
|
||||
DragEndEvent Event = "ondragend"
|
||||
)
|
||||
19
framework/hx/htmx_test.go
Normal file
19
framework/hx/htmx_test.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package hx
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewStringTrigger(t *testing.T) {
|
||||
trigger := "click once, htmx:click throttle:5, load delay:10"
|
||||
tgr := NewStringTrigger(trigger)
|
||||
assert.Equal(t, len(tgr.events), 3)
|
||||
assert.Equal(t, tgr.events[0].event, "click")
|
||||
assert.Equal(t, tgr.events[0].modifiers[0].Modifier(), "once")
|
||||
assert.Equal(t, tgr.events[1].event, "click")
|
||||
assert.Equal(t, tgr.events[1].modifiers[0].Modifier(), "throttle:5")
|
||||
assert.Equal(t, tgr.events[2].event, "load")
|
||||
assert.Equal(t, tgr.events[2].modifiers[0].Modifier(), "delay:10")
|
||||
assert.Equal(t, "click once, click throttle:5, load delay:10", tgr.ToString())
|
||||
}
|
||||
49
framework/hx/modifiers.go
Normal file
49
framework/hx/modifiers.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package hx
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Modifier interface {
|
||||
Modifier() string
|
||||
}
|
||||
|
||||
type RawModifier struct {
|
||||
modifier string
|
||||
}
|
||||
|
||||
func StringModifier(modifier string) RawModifier {
|
||||
return RawModifier{modifier}
|
||||
}
|
||||
|
||||
func (r RawModifier) Modifier() string {
|
||||
return r.modifier
|
||||
}
|
||||
|
||||
type OnceModifier struct{}
|
||||
|
||||
func (o OnceModifier) Modifier() string {
|
||||
return "once"
|
||||
}
|
||||
|
||||
type ThrottleModifier struct {
|
||||
durationSeconds int
|
||||
}
|
||||
|
||||
func (t ThrottleModifier) Modifier() string {
|
||||
return fmt.Sprintf("throttle:%ds", t.durationSeconds)
|
||||
}
|
||||
|
||||
func Throttle(durationSeconds int) ThrottleModifier {
|
||||
return ThrottleModifier{durationSeconds}
|
||||
}
|
||||
|
||||
type DelayModifier struct {
|
||||
durationSeconds int
|
||||
}
|
||||
|
||||
func (t DelayModifier) Modifier() string {
|
||||
return fmt.Sprintf("delay:%ds", t.durationSeconds)
|
||||
}
|
||||
|
||||
func Delay(durationSeconds int) DelayModifier {
|
||||
return DelayModifier{durationSeconds}
|
||||
}
|
||||
80
framework/hx/trigger.go
Normal file
80
framework/hx/trigger.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package hx
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Trigger struct {
|
||||
events []TriggerEvent
|
||||
}
|
||||
|
||||
type TriggerEvent struct {
|
||||
event Event
|
||||
modifiers []Modifier
|
||||
}
|
||||
|
||||
func NewTrigger(opts ...TriggerEvent) *Trigger {
|
||||
t := Trigger{
|
||||
events: make([]TriggerEvent, 0),
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
t.events = opts
|
||||
}
|
||||
return &t
|
||||
}
|
||||
|
||||
func NewStringTrigger(trigger string) Trigger {
|
||||
t := Trigger{
|
||||
events: make([]TriggerEvent, 0),
|
||||
}
|
||||
|
||||
split := strings.Split(trigger, ", ")
|
||||
for _, s := range split {
|
||||
parts := strings.Split(s, " ")
|
||||
event := parts[0]
|
||||
|
||||
if strings.HasPrefix(event, "htmx:") {
|
||||
event = event[5:]
|
||||
}
|
||||
|
||||
modifiers := make([]Modifier, 0)
|
||||
if len(parts) > 1 {
|
||||
for _, m := range parts[1:] {
|
||||
modifiers = append(modifiers, RawModifier{modifier: m})
|
||||
}
|
||||
}
|
||||
t.events = append(t.events, TriggerEvent{
|
||||
event: event,
|
||||
modifiers: modifiers,
|
||||
})
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t Trigger) AddEvent(event TriggerEvent) Trigger {
|
||||
t.events = append(t.events, event)
|
||||
return t
|
||||
}
|
||||
|
||||
func (t Trigger) ToString() string {
|
||||
builder := strings.Builder{}
|
||||
for i, e := range t.events {
|
||||
eventName := e.event
|
||||
if strings.HasPrefix(eventName, "htmx:") {
|
||||
eventName = eventName[5:]
|
||||
}
|
||||
builder.WriteString(eventName)
|
||||
for _, m := range e.modifiers {
|
||||
builder.WriteString(" ")
|
||||
builder.WriteString(m.Modifier())
|
||||
}
|
||||
if i < len(t.events)-1 {
|
||||
builder.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func (t Trigger) Render(builder *strings.Builder) {
|
||||
builder.WriteString(t.ToString())
|
||||
}
|
||||
16
framework/hx/triggers.go
Normal file
16
framework/hx/triggers.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package hx
|
||||
|
||||
// TriggerClick Common trigger events
|
||||
const TriggerClick = ClickEvent
|
||||
const TriggerClickOnce = TriggerClick + " once"
|
||||
const TriggerDblClick = DblClickEvent
|
||||
const TriggerKeyUpEnter = "keyup[keyCode==13]"
|
||||
const TriggerEnterPressed = TriggerKeyUpEnter
|
||||
const TriggerBlur = "blur"
|
||||
const TriggerEvery1s = "every:1s"
|
||||
const TriggerEvery2s = "every:2s"
|
||||
const TriggerEvery5s = "every:5s"
|
||||
const TriggerEvery10s = "every:10s"
|
||||
const TriggerEvery30s = "every:30s"
|
||||
const TriggerEvery1m = "every:1m"
|
||||
const TriggerLoad = LoadEvent
|
||||
|
|
@ -3,22 +3,22 @@ module htmgo-site
|
|||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/labstack/echo/v4 v4.12.0
|
||||
github.com/maddalax/htmgo/framework v0.0.0-20240920021308-279a3c716342
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/yuin/goldmark v1.7.4
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma/v2 v2.2.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/yuin/goldmark v1.7.4 // indirect
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ import (
|
|||
"embed"
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
"github.com/maddalax/htmgo/framework/htmgo/service"
|
||||
"github.com/maddalax/htmgo/framework/hx"
|
||||
"htmgo-site/internal/markdown"
|
||||
"htmgo-site/pages/base"
|
||||
"htmgo-site/partials"
|
||||
)
|
||||
|
||||
func MarkdownHandler(ctx *h.RequestContext, path string) error {
|
||||
|
|
@ -15,6 +17,9 @@ func MarkdownHandler(ctx *h.RequestContext, path string) error {
|
|||
func MarkdownPage(ctx *h.RequestContext, path string) *h.Element {
|
||||
return base.RootPage(
|
||||
h.Div(
|
||||
h.Div(
|
||||
h.GetPartial(partials.TestPartial, hx.LoadEvent),
|
||||
),
|
||||
h.Class("w-full p-4 flex flex-col justify-center items-center"),
|
||||
MarkdownContent(ctx, path),
|
||||
h.Div(
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ func GetPartialFromContext(ctx echo.Context) *h.Partial {
|
|||
cc := ctx.(*h.RequestContext)
|
||||
return partials.ToggleNavbar(cc)
|
||||
}
|
||||
if path == "TestPartial" || path == "/htmgo-site/partials.TestPartial" {
|
||||
cc := ctx.(*h.RequestContext)
|
||||
return partials.TestPartial(cc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package partials
|
||||
|
||||
import "github.com/maddalax/htmgo/framework/h"
|
||||
import (
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
)
|
||||
|
||||
type NavItem struct {
|
||||
Name string
|
||||
|
|
@ -13,6 +15,12 @@ func ToggleNavbar(ctx *h.RequestContext) *h.Partial {
|
|||
)
|
||||
}
|
||||
|
||||
func TestPartial(ctx *h.RequestContext) *h.Partial {
|
||||
return h.NewPartial(
|
||||
h.Div(h.Text("This is a test")),
|
||||
)
|
||||
}
|
||||
|
||||
var navItems = []NavItem{
|
||||
{Name: "Docs", Url: "/docs"},
|
||||
{Name: "Examples", Url: "/examples"},
|
||||
|
|
@ -37,6 +45,7 @@ func NavBar(expanded bool) *h.Element {
|
|||
)
|
||||
|
||||
desktopNav := h.Nav(
|
||||
h.Script("https://buttons.github.io/buttons.js"),
|
||||
h.Class("hidden sm:block bg-neutral-100 border border-b-slate-300 p-4 md:p-3"),
|
||||
h.Div(
|
||||
h.Class("max-w-[95%] md:max-w-prose mx-auto"),
|
||||
|
|
@ -45,6 +54,7 @@ func NavBar(expanded bool) *h.Element {
|
|||
h.Div(
|
||||
h.Class("flex items-center"),
|
||||
h.A(
|
||||
h.Boost(),
|
||||
h.Class("text-2xl"),
|
||||
h.Href("/"),
|
||||
h.Text("htmgo"),
|
||||
|
|
@ -55,6 +65,7 @@ func NavBar(expanded bool) *h.Element {
|
|||
return h.Div(
|
||||
h.Class("flex items-center"),
|
||||
h.A(
|
||||
h.Boost(),
|
||||
h.Class(""),
|
||||
h.Href(item.Url),
|
||||
h.Text(item.Name),
|
||||
|
|
@ -87,6 +98,7 @@ func MobileNav(expanded bool) *h.Element {
|
|||
h.Div(
|
||||
h.Class("flex items-center"),
|
||||
h.A(
|
||||
h.Boost(),
|
||||
h.Class("text-2xl"),
|
||||
h.Href("/"),
|
||||
h.Text("htmgo"),
|
||||
|
|
@ -94,8 +106,19 @@ func MobileNav(expanded bool) *h.Element {
|
|||
h.Div(
|
||||
h.Class("flex items-center"),
|
||||
h.Button(
|
||||
h.GetPartialWithQs(ToggleNavbar, h.Ternary(expanded, "expanded=false", "expanded=true")),
|
||||
h.Trigger(h.TriggerClick),
|
||||
h.Boost(),
|
||||
|
||||
h.GetPartialWithQs(
|
||||
ToggleNavbar,
|
||||
h.NewQs("expanded", h.Ternary(expanded, "false", "true"), "test", "true"),
|
||||
"click",
|
||||
),
|
||||
|
||||
h.AttributePairs(
|
||||
"class", "text-2xl",
|
||||
"aria-expanded", h.Ternary(expanded, "true", "false"),
|
||||
),
|
||||
|
||||
h.Class("text-2xl"),
|
||||
h.Text("☰"),
|
||||
),
|
||||
|
|
@ -109,6 +132,7 @@ func MobileNav(expanded bool) *h.Element {
|
|||
return h.Div(
|
||||
h.Class("flex items-center"),
|
||||
h.A(
|
||||
h.Boost(),
|
||||
h.Class(""),
|
||||
h.Href(item.Url),
|
||||
h.Text(item.Name),
|
||||
|
|
|
|||
Loading…
Reference in a new issue