fix todo mvc
This commit is contained in:
parent
da7e22c446
commit
1f98f49735
6 changed files with 57 additions and 55 deletions
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Headers = map[string]string
|
|
||||||
|
|
||||||
type Partial struct {
|
type Partial struct {
|
||||||
Headers *Headers
|
Headers *Headers
|
||||||
Root *Element
|
Root *Element
|
||||||
|
|
@ -51,6 +49,13 @@ func NewPartial(root *Element) *Partial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SwapManyPartialWithHeaders(ctx *RequestContext, headers *Headers, swaps ...*Element) *Partial {
|
||||||
|
return NewPartialWithHeaders(
|
||||||
|
headers,
|
||||||
|
SwapMany(ctx, swaps...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func SwapManyPartial(ctx *RequestContext, swaps ...*Element) *Partial {
|
func SwapManyPartial(ctx *RequestContext, swaps ...*Element) *Partial {
|
||||||
return NewPartial(
|
return NewPartial(
|
||||||
SwapMany(ctx, swaps...),
|
SwapMany(ctx, swaps...),
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,16 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Headers = map[string]string
|
||||||
|
|
||||||
func ReplaceUrlHeader(url string) *Headers {
|
func ReplaceUrlHeader(url string) *Headers {
|
||||||
return NewHeaders(hx.ReplaceUrlHeader, url)
|
return NewHeaders(hx.ReplaceUrlHeader, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PushUrlHeader(url string) *Headers {
|
||||||
|
return NewHeaders(hx.PushUrlHeader, url)
|
||||||
|
}
|
||||||
|
|
||||||
func CombineHeaders(headers ...*Headers) *Headers {
|
func CombineHeaders(headers ...*Headers) *Headers {
|
||||||
m := make(Headers)
|
m := make(Headers)
|
||||||
for _, h := range headers {
|
for _, h := range headers {
|
||||||
|
|
|
||||||
|
|
@ -90,28 +90,10 @@ 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) {
|
func (l *LifeCycle) fromAttributeMap(event string, key string, value string, builder *strings.Builder) {
|
||||||
|
|
||||||
if key == hx.GetAttr || key == hx.PatchAttr || key == hx.PostAttr {
|
if key == hx.GetAttr || key == hx.PatchAttr || key == hx.PostAttr {
|
||||||
TriggerString(toHtmxTriggerName(event)).Render(builder)
|
TriggerString(hx.ToHtmxTriggerName(event)).Render(builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
Attribute(key, value).Render(builder)
|
Attribute(key, value).Render(builder)
|
||||||
|
|
@ -125,7 +107,7 @@ func (l *LifeCycle) Render(builder *strings.Builder) {
|
||||||
for _, command := range commands {
|
for _, command := range commands {
|
||||||
switch c := command.(type) {
|
switch c := command.(type) {
|
||||||
case JsCommand:
|
case JsCommand:
|
||||||
eventName := formatEventName(event, true)
|
eventName := hx.FormatEventName(event, true)
|
||||||
m[eventName] += fmt.Sprintf("%s;", c.Command)
|
m[eventName] += fmt.Sprintf("%s;", c.Command)
|
||||||
case *AttributeMap:
|
case *AttributeMap:
|
||||||
for k, v := range c.ToMap() {
|
for k, v := range c.ToMap() {
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,32 @@ type TriggerEvent struct {
|
||||||
modifiers []Modifier
|
modifiers []Modifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 NewTrigger(opts ...TriggerEvent) *Trigger {
|
func NewTrigger(opts ...TriggerEvent) *Trigger {
|
||||||
t := Trigger{
|
t := Trigger{
|
||||||
events: make([]TriggerEvent, 0),
|
events: make([]TriggerEvent, 0),
|
||||||
}
|
}
|
||||||
if len(opts) > 0 {
|
if len(opts) > 0 {
|
||||||
|
for i, opt := range opts {
|
||||||
|
opts[i].event = ToHtmxTriggerName(opt.event)
|
||||||
|
}
|
||||||
t.events = opts
|
t.events = opts
|
||||||
}
|
}
|
||||||
return &t
|
return &t
|
||||||
|
|
@ -31,11 +52,7 @@ func NewStringTrigger(trigger string) Trigger {
|
||||||
split := strings.Split(trigger, ", ")
|
split := strings.Split(trigger, ", ")
|
||||||
for _, s := range split {
|
for _, s := range split {
|
||||||
parts := strings.Split(s, " ")
|
parts := strings.Split(s, " ")
|
||||||
event := parts[0]
|
event := ToHtmxTriggerName(parts[0])
|
||||||
|
|
||||||
if strings.HasPrefix(event, "htmx:") {
|
|
||||||
event = event[5:]
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiers := make([]Modifier, 0)
|
modifiers := make([]Modifier, 0)
|
||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,6 @@ import (
|
||||||
"todolist/partials/load"
|
"todolist/partials/load"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CustomContext struct {
|
|
||||||
echo.Context
|
|
||||||
locator *service.Locator
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CustomContext) ServiceLocator() *service.Locator {
|
|
||||||
return c.locator
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
locator := service.NewLocator()
|
locator := service.NewLocator()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/maddalax/htmgo/framework/h"
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"github.com/maddalax/htmgo/framework/hx"
|
||||||
"todolist/ent"
|
"todolist/ent"
|
||||||
"todolist/internal/tasks"
|
"todolist/internal/tasks"
|
||||||
)
|
)
|
||||||
|
|
@ -55,7 +56,7 @@ func Input(list []*ent.Task) *h.Element {
|
||||||
h.Class("pl-12 text-xl p-4 w-full outline-none focus:outline-2 focus:outline-rose-400"),
|
h.Class("pl-12 text-xl p-4 w-full outline-none focus:outline-2 focus:outline-rose-400"),
|
||||||
h.Placeholder("What needs to be done?"),
|
h.Placeholder("What needs to be done?"),
|
||||||
h.Post(h.GetPartialPath(Create)),
|
h.Post(h.GetPartialPath(Create)),
|
||||||
h.HxTrigger("keyup[keyCode==13]"),
|
h.HxTrigger(hx.OnEvent(hx.TriggerKeyUpEnter)),
|
||||||
),
|
),
|
||||||
CompleteAllIcon(list),
|
CompleteAllIcon(list),
|
||||||
)
|
)
|
||||||
|
|
@ -70,7 +71,7 @@ func CompleteAllIcon(list []*ent.Task) *h.Element {
|
||||||
h.ClassX("absolute top-0 left-0 p-4 rotate-90 text-2xl cursor-pointer", map[string]bool{
|
h.ClassX("absolute top-0 left-0 p-4 rotate-90 text-2xl cursor-pointer", map[string]bool{
|
||||||
"text-slate-400": notCompletedCount > 0,
|
"text-slate-400": notCompletedCount > 0,
|
||||||
}), h.Text("❯"),
|
}), h.Text("❯"),
|
||||||
h.PostPartialOnClickQs(CompleteAll, h.Ternary(notCompletedCount > 0, "complete=true", "complete=false")),
|
h.PostPartialWithQs(CompleteAll, h.NewQs("complete", h.Ternary(notCompletedCount > 0, "true", "false"))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +93,7 @@ func Footer(list []*ent.Task, activeTab Tab) *h.Element {
|
||||||
h.Class("flex items-center gap-4"),
|
h.Class("flex items-center gap-4"),
|
||||||
h.List(tabs, func(tab Tab, index int) *h.Element {
|
h.List(tabs, func(tab Tab, index int) *h.Element {
|
||||||
return h.P(
|
return h.P(
|
||||||
h.PostOnClick(h.GetPartialPathWithQs(ChangeTab, "tab="+tab)),
|
h.PostOnClick(h.GetPartialPathWithQs(ChangeTab, h.NewQs("tab", tab))),
|
||||||
h.ClassX("cursor-pointer px-2 py-1 rounded", map[string]bool{
|
h.ClassX("cursor-pointer px-2 py-1 rounded", map[string]bool{
|
||||||
"border border-rose-600": activeTab == tab,
|
"border border-rose-600": activeTab == tab,
|
||||||
}),
|
}),
|
||||||
|
|
@ -147,7 +148,7 @@ func Task(task *ent.Task, editing bool) *h.Element {
|
||||||
),
|
),
|
||||||
h.Input(
|
h.Input(
|
||||||
"text",
|
"text",
|
||||||
h.PostPartialOnTrigger(UpdateName, h.TriggerBlur, h.TriggerKeyUpEnter),
|
h.PostPartial(UpdateName, hx.TriggerBlur, hx.TriggerKeyUpEnter),
|
||||||
h.Attributes(&h.AttributeMap{
|
h.Attributes(&h.AttributeMap{
|
||||||
"placeholder": "What needs to be done?",
|
"placeholder": "What needs to be done?",
|
||||||
"autofocus": "true",
|
"autofocus": "true",
|
||||||
|
|
@ -162,8 +163,7 @@ func Task(task *ent.Task, editing bool) *h.Element {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
h.P(
|
h.P(
|
||||||
h.HxTrigger("dblclick"),
|
h.GetPartialWithQs(EditNameForm, h.NewQs("id", task.ID.String()), hx.TriggerDblClick),
|
||||||
h.GetPartialWithQs(EditNameForm, "id="+task.ID.String()),
|
|
||||||
h.ClassX("text-xl break-all text-wrap truncate", map[string]bool{
|
h.ClassX("text-xl break-all text-wrap truncate", map[string]bool{
|
||||||
"line-through text-slate-400": task.CompletedAt != nil,
|
"line-through text-slate-400": task.CompletedAt != nil,
|
||||||
}),
|
}),
|
||||||
|
|
@ -174,8 +174,8 @@ func Task(task *ent.Task, editing bool) *h.Element {
|
||||||
|
|
||||||
func CompleteIcon(task *ent.Task) *h.Element {
|
func CompleteIcon(task *ent.Task) *h.Element {
|
||||||
return h.Div(
|
return h.Div(
|
||||||
h.HxTrigger("click"),
|
h.HxTrigger(hx.OnClick()),
|
||||||
h.Post(h.GetPartialPathWithQs(ToggleCompleted, "id="+task.ID.String())),
|
h.Post(h.GetPartialPathWithQs(ToggleCompleted, h.NewQs("id", task.ID.String()))),
|
||||||
h.Class("flex items-center justify-center cursor-pointer"),
|
h.Class("flex items-center justify-center cursor-pointer"),
|
||||||
h.Div(
|
h.Div(
|
||||||
h.ClassX("w-10 h-10 border rounded-full flex items-center justify-center", map[string]bool{
|
h.ClassX("w-10 h-10 border rounded-full flex items-center justify-center", map[string]bool{
|
||||||
|
|
@ -259,11 +259,11 @@ func ToggleCompleted(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
||||||
list, _ := service.List()
|
list, _ := service.List()
|
||||||
|
|
||||||
return h.NewPartial(h.Fragment(
|
return h.SwapManyPartial(ctx,
|
||||||
h.OobSwap(ctx, List(list, getActiveTab(ctx))),
|
List(list, getActiveTab(ctx)),
|
||||||
h.OobSwap(ctx, Footer(list, getActiveTab(ctx))),
|
Footer(list, getActiveTab(ctx)),
|
||||||
h.OobSwap(ctx, CompleteAllIcon(list)),
|
CompleteAllIcon(list),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CompleteAll(ctx *h.RequestContext) *h.Partial {
|
func CompleteAll(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
@ -302,7 +302,9 @@ func Create(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
||||||
list, _ := service.List()
|
list, _ := service.List()
|
||||||
|
|
||||||
return h.NewPartial(h.Fragment(h.OobSwap(ctx, CardBody(list, getActiveTab(ctx)))))
|
return h.SwapManyPartial(ctx,
|
||||||
|
CardBody(list, getActiveTab(ctx)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChangeTab(ctx *h.RequestContext) *h.Partial {
|
func ChangeTab(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
@ -311,10 +313,9 @@ func ChangeTab(ctx *h.RequestContext) *h.Partial {
|
||||||
|
|
||||||
tab := ctx.QueryParam("tab")
|
tab := ctx.QueryParam("tab")
|
||||||
|
|
||||||
return h.NewPartialWithHeaders(&h.Headers{"hx-push-url": fmt.Sprintf("/tasks?tab=%s", tab)},
|
return h.SwapManyPartialWithHeaders(ctx,
|
||||||
h.Fragment(
|
h.PushUrlHeader(fmt.Sprintf("/tasks?tab=%s", tab)),
|
||||||
h.OobSwap(ctx, List(list, tab)),
|
List(list, tab),
|
||||||
h.OobSwap(ctx, Footer(list, tab)),
|
Footer(list, tab),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue