From 8aa91c577cbd81bf307f21a2e4a0995af25334c6 Mon Sep 17 00:00:00 2001 From: Ryan Hamamura <58859899+ryanhamamura@users.noreply.github.com> Date: Fri, 6 Feb 2026 10:54:27 -1000 Subject: [PATCH] feat: add event types OnSubmit, OnInput, OnFocus, OnBlur, OnMouseEnter, OnMouseLeave, OnScroll, OnDblClick --- actiontrigger.go | 48 ++++++++++++++++++++++++++++++++++++++++++ via_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/actiontrigger.go b/actiontrigger.go index 7f47a4f..17309c8 100644 --- a/actiontrigger.go +++ b/actiontrigger.go @@ -107,6 +107,54 @@ func (a *actionTrigger) OnChange(options ...ActionTriggerOption) h.H { return h.Data("on:change__debounce.200ms", buildOnExpr(actionURL(a.id), &opts)) } +// OnSubmit returns a via.h DOM attribute that triggers on form submit. +func (a *actionTrigger) OnSubmit(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:submit", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnInput returns a via.h DOM attribute that triggers on input (without debounce). +func (a *actionTrigger) OnInput(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:input", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnFocus returns a via.h DOM attribute that triggers when the element gains focus. +func (a *actionTrigger) OnFocus(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:focus", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnBlur returns a via.h DOM attribute that triggers when the element loses focus. +func (a *actionTrigger) OnBlur(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:blur", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnMouseEnter returns a via.h DOM attribute that triggers when the mouse enters the element. +func (a *actionTrigger) OnMouseEnter(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:mouseenter", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnMouseLeave returns a via.h DOM attribute that triggers when the mouse leaves the element. +func (a *actionTrigger) OnMouseLeave(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:mouseleave", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnScroll returns a via.h DOM attribute that triggers on scroll. +func (a *actionTrigger) OnScroll(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:scroll", buildOnExpr(actionURL(a.id), &opts)) +} + +// OnDblClick returns a via.h DOM attribute that triggers on double click. +func (a *actionTrigger) OnDblClick(options ...ActionTriggerOption) h.H { + opts := applyOptions(options...) + return h.Data("on:dblclick", buildOnExpr(actionURL(a.id), &opts)) +} + // OnKeyDown returns a via.h DOM attribute that triggers when a key is pressed. // key: optional, see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key // Example: OnKeyDown("Enter") diff --git a/via_test.go b/via_test.go index 4891295..190eb14 100644 --- a/via_test.go +++ b/via_test.go @@ -132,6 +132,60 @@ func TestAction(t *testing.T) { assert.Contains(t, body, "/_action/") } +func TestEventTypes(t *testing.T) { + tests := []struct { + name string + attr string + buildEl func(trigger *actionTrigger) h.H + }{ + {"OnSubmit", "data-on:submit", func(tr *actionTrigger) h.H { return h.Form(tr.OnSubmit()) }}, + {"OnInput", "data-on:input", func(tr *actionTrigger) h.H { return h.Input(tr.OnInput()) }}, + {"OnFocus", "data-on:focus", func(tr *actionTrigger) h.H { return h.Input(tr.OnFocus()) }}, + {"OnBlur", "data-on:blur", func(tr *actionTrigger) h.H { return h.Input(tr.OnBlur()) }}, + {"OnMouseEnter", "data-on:mouseenter", func(tr *actionTrigger) h.H { return h.Div(tr.OnMouseEnter()) }}, + {"OnMouseLeave", "data-on:mouseleave", func(tr *actionTrigger) h.H { return h.Div(tr.OnMouseLeave()) }}, + {"OnScroll", "data-on:scroll", func(tr *actionTrigger) h.H { return h.Div(tr.OnScroll()) }}, + {"OnDblClick", "data-on:dblclick", func(tr *actionTrigger) h.H { return h.Div(tr.OnDblClick()) }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var trigger *actionTrigger + v := New() + v.Page("/", func(c *Context) { + trigger = c.Action(func() {}) + c.View(func() h.H { return tt.buildEl(trigger) }) + }) + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + v.mux.ServeHTTP(w, req) + body := w.Body.String() + assert.Contains(t, body, tt.attr) + assert.Contains(t, body, "/_action/"+trigger.id) + }) + } + + t.Run("WithSignal", func(t *testing.T) { + var trigger *actionTrigger + var sig *signal + v := New() + v.Page("/", func(c *Context) { + trigger = c.Action(func() {}) + sig = c.Signal("val") + c.View(func() h.H { + return h.Div(trigger.OnDblClick(WithSignal(sig, "x"))) + }) + }) + + req := httptest.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + v.mux.ServeHTTP(w, req) + body := w.Body.String() + assert.Contains(t, body, "data-on:dblclick") + assert.Contains(t, body, "$"+sig.ID()+"='x'") + }) +} + func TestOnKeyDownWithWindow(t *testing.T) { var trigger *actionTrigger v := New()