scripting enhancements
This commit is contained in:
parent
10f48af304
commit
cd8d08c0a5
8 changed files with 229 additions and 11 deletions
|
|
@ -2,6 +2,7 @@ package h
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/maddalax/htmgo/framework/hx"
|
||||
"github.com/maddalax/htmgo/framework/internal/util"
|
||||
"strings"
|
||||
|
|
@ -233,6 +234,54 @@ func ToggleClass(class string) SimpleJsCommand {
|
|||
return SimpleJsCommand{Command: fmt.Sprintf("this.classList.toggle('%s')", class)}
|
||||
}
|
||||
|
||||
// ToggleText toggles the given text on the element.
|
||||
func ToggleText(text string, textTwo string) Command {
|
||||
// language=JavaScript
|
||||
return EvalJs(fmt.Sprintf(`
|
||||
if(self.innerText === "%s") {
|
||||
self.innerText = "%s";
|
||||
} else {
|
||||
self.innerText = "%s";
|
||||
}
|
||||
`, text, textTwo, text))
|
||||
}
|
||||
|
||||
// ToggleTextOnSibling toggles the given text on the siblings of the element.
|
||||
func ToggleTextOnSibling(selector, text string, textTwo string) Command {
|
||||
// language=JavaScript
|
||||
return EvalJsOnSibling(selector, fmt.Sprintf(`
|
||||
if(element.innerText === "%s") {
|
||||
element.innerText = "%s";
|
||||
} else {
|
||||
element.innerText = "%s";
|
||||
}
|
||||
`, text, textTwo, text))
|
||||
}
|
||||
|
||||
// ToggleTextOnChildren toggles the given text on the children of the element.
|
||||
func ToggleTextOnChildren(selector, text string, textTwo string) Command {
|
||||
// language=JavaScript
|
||||
return EvalJsOnChildren(selector, fmt.Sprintf(`
|
||||
if(element.innerText === "%s") {
|
||||
element.innerText = "%s";
|
||||
} else {
|
||||
element.innerText = "%s";
|
||||
}
|
||||
`, text, textTwo, text))
|
||||
}
|
||||
|
||||
// ToggleTextOnParent toggles the given text on the parent of the element.
|
||||
func ToggleTextOnParent(text string, textTwo string) Command {
|
||||
// language=JavaScript
|
||||
return EvalJsOnParent(fmt.Sprintf(`
|
||||
if(element.innerText === "%s") {
|
||||
element.innerText = "%s";
|
||||
} else {
|
||||
element.innerText = "%s";
|
||||
}
|
||||
`, text, textTwo, text))
|
||||
}
|
||||
|
||||
// ToggleClassOnElement toggles the given class on the elements returned by the selector.
|
||||
func ToggleClassOnElement(selector, class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
|
|
@ -276,24 +325,42 @@ func EvalJsOnSibling(selector, js string) ComplexJsCommand {
|
|||
`, selector, js))
|
||||
}
|
||||
|
||||
// SetClassOnParent sets the given class on the parent of the element. Reference the element using 'element'.
|
||||
// SetClassOnParent sets the given class on the parent of the element.
|
||||
func SetClassOnParent(class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
return EvalJsOnParent(fmt.Sprintf("element.classList.add('%s')", class))
|
||||
}
|
||||
|
||||
// RemoveClassOnParent removes the given class from the parent of the element. Reference the element using 'element'.
|
||||
// RemoveClassOnParent removes the given class from the parent of the element.
|
||||
func RemoveClassOnParent(class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
return EvalJsOnParent(fmt.Sprintf("element.classList.remove('%s')", class))
|
||||
}
|
||||
|
||||
// SetClassOnChildren sets the given class on the children of the element. Reference the element using 'element'.
|
||||
// SetClassOnChildren sets the given class on the children of the element.
|
||||
func SetClassOnChildren(selector, class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
return EvalJsOnChildren(selector, fmt.Sprintf("element.classList.add('%s')", class))
|
||||
}
|
||||
|
||||
// ToggleClassOnChildren toggles the given class on the children of the element.
|
||||
func ToggleClassOnChildren(selector, class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
return EvalJsOnChildren(selector, fmt.Sprintf("element.classList.toggle('%s')", class))
|
||||
}
|
||||
|
||||
// ToggleClassOnParent toggles the given class on the parent of the element.
|
||||
func ToggleClassOnParent(class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
return EvalJsOnParent(fmt.Sprintf("element.classList.toggle('%s')", class))
|
||||
}
|
||||
|
||||
// ToggleClassOnSibling toggles the given class on the siblings of the element.
|
||||
func ToggleClassOnSibling(selector, class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
return EvalJsOnSibling(selector, fmt.Sprintf("element.classList.toggle('%s')", class))
|
||||
}
|
||||
|
||||
// SetClassOnSibling sets the given class on the siblings of the element. Reference the element using 'element'.
|
||||
func SetClassOnSibling(selector, class string) ComplexJsCommand {
|
||||
// language=JavaScript
|
||||
|
|
@ -330,6 +397,36 @@ func EvalJs(js string) ComplexJsCommand {
|
|||
return NewComplexJsCommand(js)
|
||||
}
|
||||
|
||||
func EvalCommandsOnSelector(selector string, cmds ...Command) ComplexJsCommand {
|
||||
lines := make([]string, len(cmds))
|
||||
for i, cmd := range cmds {
|
||||
lines[i] = Render(cmd)
|
||||
lines[i] = strings.ReplaceAll(lines[i], "this.", "self.")
|
||||
// some commands set the element we need to fix it so we arent redeclaring it
|
||||
lines[i] = strings.ReplaceAll(lines[i], "let element =", "element =")
|
||||
}
|
||||
code := strings.Join(lines, "\n")
|
||||
return EvalJs(fmt.Sprintf(`
|
||||
let element = document.querySelector("%s");
|
||||
|
||||
if(!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
self = element;
|
||||
%s
|
||||
`, selector, code))
|
||||
}
|
||||
|
||||
func EvalCommands(element *Element, cmds ...Command) ComplexJsCommand {
|
||||
id := strings.ReplaceAll(uuid.NewString(), "-", "")
|
||||
element.AppendChildren(
|
||||
Attribute("data-eval-commands-id", id),
|
||||
)
|
||||
return EvalCommandsOnSelector(
|
||||
fmt.Sprintf(`[data-eval-commands-id='%s']`, id), cmds...)
|
||||
}
|
||||
|
||||
// PreventDefault prevents the default action of the event.
|
||||
func PreventDefault() SimpleJsCommand {
|
||||
// language=JavaScript
|
||||
|
|
|
|||
|
|
@ -35,9 +35,15 @@ var voidTags = map[string]bool{
|
|||
"wbr": true,
|
||||
}
|
||||
|
||||
type ScriptEntry struct {
|
||||
Body string
|
||||
ChildOf *Element
|
||||
}
|
||||
|
||||
type RenderContext struct {
|
||||
builder *strings.Builder
|
||||
scripts []string
|
||||
builder *strings.Builder
|
||||
scripts []ScriptEntry
|
||||
currentElement *Element
|
||||
}
|
||||
|
||||
func (ctx *RenderContext) AddScript(funcName string, body string) {
|
||||
|
|
@ -48,7 +54,11 @@ func (ctx *RenderContext) AddScript(funcName string, body string) {
|
|||
%s
|
||||
}
|
||||
</script>`, funcName, funcName, body)
|
||||
ctx.scripts = append(ctx.scripts, script)
|
||||
|
||||
ctx.scripts = append(ctx.scripts, ScriptEntry{
|
||||
Body: script,
|
||||
ChildOf: ctx.currentElement,
|
||||
})
|
||||
}
|
||||
|
||||
func (node *Element) Render(context *RenderContext) {
|
||||
|
|
@ -56,6 +66,8 @@ func (node *Element) Render(context *RenderContext) {
|
|||
return
|
||||
}
|
||||
|
||||
context.currentElement = node
|
||||
|
||||
if node.tag == CachedNodeTag {
|
||||
meta := node.meta.(*CachedNode)
|
||||
meta.Render(context)
|
||||
|
|
@ -147,7 +159,7 @@ func (node *Element) Render(context *RenderContext) {
|
|||
}
|
||||
|
||||
if node.tag != "" {
|
||||
renderScripts(context)
|
||||
renderScripts(context, node)
|
||||
if !voidTags[node.tag] {
|
||||
context.builder.WriteString("</")
|
||||
context.builder.WriteString(node.tag)
|
||||
|
|
@ -156,11 +168,19 @@ func (node *Element) Render(context *RenderContext) {
|
|||
}
|
||||
}
|
||||
|
||||
func renderScripts(context *RenderContext) {
|
||||
for _, script := range context.scripts {
|
||||
context.builder.WriteString(script)
|
||||
func renderScripts(context *RenderContext, parent *Element) {
|
||||
if len(context.scripts) == 0 {
|
||||
return
|
||||
}
|
||||
context.scripts = []string{}
|
||||
notWritten := make([]ScriptEntry, 0)
|
||||
for _, script := range context.scripts {
|
||||
if script.ChildOf == parent {
|
||||
context.builder.WriteString(script.Body)
|
||||
} else {
|
||||
notWritten = append(notWritten, script)
|
||||
}
|
||||
}
|
||||
context.scripts = notWritten
|
||||
}
|
||||
|
||||
func (a *AttributeR) Render(context *RenderContext) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ var SetDisabled = h.SetDisabled
|
|||
var RemoveClass = h.RemoveClass
|
||||
var Alert = h.Alert
|
||||
var SetClassOnChildren = h.SetClassOnChildren
|
||||
var ToggleClassOnChildren = h.ToggleClassOnChildren
|
||||
var ToggleClassOnParent = h.ToggleClassOnParent
|
||||
var ToggleClassOnSibling = h.ToggleClassOnSibling
|
||||
var RemoveClassOnChildren = h.RemoveClassOnChildren
|
||||
var EvalJsOnChildren = h.EvalJsOnChildren
|
||||
var EvalJsOnSibling = h.EvalJsOnSibling
|
||||
|
|
@ -23,6 +26,8 @@ var RemoveClassOnSibling = h.RemoveClassOnSibling
|
|||
var Remove = h.Remove
|
||||
var PreventDefault = h.PreventDefault
|
||||
var EvalJs = h.EvalJs
|
||||
var EvalCommands = h.EvalCommands
|
||||
var EvalCommandsOnSelector = h.EvalCommandsOnSelector
|
||||
var ConsoleLog = h.ConsoleLog
|
||||
var SetValue = h.SetValue
|
||||
var SubmitFormOnEnter = h.SubmitFormOnEnter
|
||||
|
|
@ -36,3 +41,7 @@ var GetWithQs = h.GetWithQs
|
|||
var PostWithQs = h.PostWithQs
|
||||
var ToggleClass = h.ToggleClass
|
||||
var ToggleClassOnElement = h.ToggleClassOnElement
|
||||
var ToggleText = h.ToggleText
|
||||
var ToggleTextOnSibling = h.ToggleTextOnSibling
|
||||
var ToggleTextOnChildren = h.ToggleTextOnChildren
|
||||
var ToggleTextOnParent = h.ToggleTextOnParent
|
||||
|
|
|
|||
|
|
@ -70,9 +70,31 @@ var ClickToEditSnippet = Snippet{
|
|||
partial: snippets.ClickToEdit,
|
||||
}
|
||||
|
||||
var JsSetTextOnClick = Snippet{
|
||||
category: "Interactivity (JS)",
|
||||
name: "Set Element Text On Click",
|
||||
description: "A simple example of how to use htmgo with javascript",
|
||||
sidebarName: "Set Text On Click",
|
||||
path: "/examples/js-set-text-on-click",
|
||||
partial: snippets.SetTextOnClick,
|
||||
}
|
||||
|
||||
var JsHideChildrenOnClick = Snippet{
|
||||
category: "Interactivity (JS)",
|
||||
name: "Hide / Show Children On Click",
|
||||
description: "Use JS to hide and show children elements on click",
|
||||
sidebarName: "Hide / Show Children",
|
||||
path: "/examples/js-hide-children-on-click",
|
||||
partial: snippets.JsHideChildrenOnClick,
|
||||
}
|
||||
|
||||
var examples = []Snippet{
|
||||
FormWithLoadingStateSnippet,
|
||||
ClickToEditSnippet,
|
||||
|
||||
JsSetTextOnClick,
|
||||
JsHideChildrenOnClick,
|
||||
|
||||
UserAuthSnippet,
|
||||
ChatSnippet,
|
||||
HackerNewsSnippet,
|
||||
|
|
|
|||
10
htmgo-site/pages/examples/js-hide-children-on-click.go
Normal file
10
htmgo-site/pages/examples/js-hide-children-on-click.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package examples
|
||||
|
||||
import (
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
)
|
||||
|
||||
func JsHideChildrenOnClickPage(ctx *h.RequestContext) *h.Page {
|
||||
SetSnippet(ctx, &JsHideChildrenOnClick)
|
||||
return Index(ctx)
|
||||
}
|
||||
10
htmgo-site/pages/examples/js-set-text-on-click.go
Normal file
10
htmgo-site/pages/examples/js-set-text-on-click.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package examples
|
||||
|
||||
import (
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
)
|
||||
|
||||
func JsSetTextOnClickPage(ctx *h.RequestContext) *h.Page {
|
||||
SetSnippet(ctx, &JsSetTextOnClick)
|
||||
return Index(ctx)
|
||||
}
|
||||
32
htmgo-site/partials/snippets/js-hide-children-on-click.go
Normal file
32
htmgo-site/partials/snippets/js-hide-children-on-click.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package snippets
|
||||
|
||||
import (
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
"github.com/maddalax/htmgo/framework/js"
|
||||
)
|
||||
|
||||
func JsHideChildrenOnClick(ctx *h.RequestContext) *h.Partial {
|
||||
text := h.Pf("- Parent")
|
||||
return h.NewPartial(
|
||||
h.Div(
|
||||
text,
|
||||
h.Class("cursor-pointer"),
|
||||
h.Id("js-test"),
|
||||
h.OnClick(
|
||||
js.ToggleClassOnChildren("div", "hidden"),
|
||||
js.EvalCommands(
|
||||
text,
|
||||
js.ToggleText("+ Parent", "- Parent"),
|
||||
),
|
||||
),
|
||||
h.Div(
|
||||
h.Class("ml-4"),
|
||||
h.Text("Child 1"),
|
||||
),
|
||||
h.Div(
|
||||
h.Class("ml-4"),
|
||||
h.Text("Child 2"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
18
htmgo-site/partials/snippets/js-set-text-on-click.go
Normal file
18
htmgo-site/partials/snippets/js-set-text-on-click.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package snippets
|
||||
|
||||
import (
|
||||
"github.com/maddalax/htmgo/framework/h"
|
||||
"github.com/maddalax/htmgo/framework/js"
|
||||
)
|
||||
|
||||
func SetTextOnClick(ctx *h.RequestContext) *h.Partial {
|
||||
return h.NewPartial(
|
||||
h.Button(
|
||||
h.Text("Click to set text"),
|
||||
h.Class("bg-slate-900 text-white py-2 px-4 rounded"),
|
||||
h.OnClick(
|
||||
js.SetText("Hello World"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue