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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/maddalax/htmgo/framework/hx"
|
"github.com/maddalax/htmgo/framework/hx"
|
||||||
"github.com/maddalax/htmgo/framework/internal/util"
|
"github.com/maddalax/htmgo/framework/internal/util"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -233,6 +234,54 @@ func ToggleClass(class string) SimpleJsCommand {
|
||||||
return SimpleJsCommand{Command: fmt.Sprintf("this.classList.toggle('%s')", class)}
|
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.
|
// ToggleClassOnElement toggles the given class on the elements returned by the selector.
|
||||||
func ToggleClassOnElement(selector, class string) ComplexJsCommand {
|
func ToggleClassOnElement(selector, class string) ComplexJsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
|
|
@ -276,24 +325,42 @@ func EvalJsOnSibling(selector, js string) ComplexJsCommand {
|
||||||
`, selector, js))
|
`, 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 {
|
func SetClassOnParent(class string) ComplexJsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
return EvalJsOnParent(fmt.Sprintf("element.classList.add('%s')", class))
|
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 {
|
func RemoveClassOnParent(class string) ComplexJsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
return EvalJsOnParent(fmt.Sprintf("element.classList.remove('%s')", class))
|
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 {
|
func SetClassOnChildren(selector, class string) ComplexJsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
return EvalJsOnChildren(selector, fmt.Sprintf("element.classList.add('%s')", class))
|
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'.
|
// SetClassOnSibling sets the given class on the siblings of the element. Reference the element using 'element'.
|
||||||
func SetClassOnSibling(selector, class string) ComplexJsCommand {
|
func SetClassOnSibling(selector, class string) ComplexJsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
|
|
@ -330,6 +397,36 @@ func EvalJs(js string) ComplexJsCommand {
|
||||||
return NewComplexJsCommand(js)
|
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.
|
// PreventDefault prevents the default action of the event.
|
||||||
func PreventDefault() SimpleJsCommand {
|
func PreventDefault() SimpleJsCommand {
|
||||||
// language=JavaScript
|
// language=JavaScript
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,15 @@ var voidTags = map[string]bool{
|
||||||
"wbr": true,
|
"wbr": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ScriptEntry struct {
|
||||||
|
Body string
|
||||||
|
ChildOf *Element
|
||||||
|
}
|
||||||
|
|
||||||
type RenderContext struct {
|
type RenderContext struct {
|
||||||
builder *strings.Builder
|
builder *strings.Builder
|
||||||
scripts []string
|
scripts []ScriptEntry
|
||||||
|
currentElement *Element
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *RenderContext) AddScript(funcName string, body string) {
|
func (ctx *RenderContext) AddScript(funcName string, body string) {
|
||||||
|
|
@ -48,7 +54,11 @@ func (ctx *RenderContext) AddScript(funcName string, body string) {
|
||||||
%s
|
%s
|
||||||
}
|
}
|
||||||
</script>`, funcName, funcName, body)
|
</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) {
|
func (node *Element) Render(context *RenderContext) {
|
||||||
|
|
@ -56,6 +66,8 @@ func (node *Element) Render(context *RenderContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.currentElement = node
|
||||||
|
|
||||||
if node.tag == CachedNodeTag {
|
if node.tag == CachedNodeTag {
|
||||||
meta := node.meta.(*CachedNode)
|
meta := node.meta.(*CachedNode)
|
||||||
meta.Render(context)
|
meta.Render(context)
|
||||||
|
|
@ -147,7 +159,7 @@ func (node *Element) Render(context *RenderContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.tag != "" {
|
if node.tag != "" {
|
||||||
renderScripts(context)
|
renderScripts(context, node)
|
||||||
if !voidTags[node.tag] {
|
if !voidTags[node.tag] {
|
||||||
context.builder.WriteString("</")
|
context.builder.WriteString("</")
|
||||||
context.builder.WriteString(node.tag)
|
context.builder.WriteString(node.tag)
|
||||||
|
|
@ -156,11 +168,19 @@ func (node *Element) Render(context *RenderContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderScripts(context *RenderContext) {
|
func renderScripts(context *RenderContext, parent *Element) {
|
||||||
for _, script := range context.scripts {
|
if len(context.scripts) == 0 {
|
||||||
context.builder.WriteString(script)
|
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) {
|
func (a *AttributeR) Render(context *RenderContext) {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ var SetDisabled = h.SetDisabled
|
||||||
var RemoveClass = h.RemoveClass
|
var RemoveClass = h.RemoveClass
|
||||||
var Alert = h.Alert
|
var Alert = h.Alert
|
||||||
var SetClassOnChildren = h.SetClassOnChildren
|
var SetClassOnChildren = h.SetClassOnChildren
|
||||||
|
var ToggleClassOnChildren = h.ToggleClassOnChildren
|
||||||
|
var ToggleClassOnParent = h.ToggleClassOnParent
|
||||||
|
var ToggleClassOnSibling = h.ToggleClassOnSibling
|
||||||
var RemoveClassOnChildren = h.RemoveClassOnChildren
|
var RemoveClassOnChildren = h.RemoveClassOnChildren
|
||||||
var EvalJsOnChildren = h.EvalJsOnChildren
|
var EvalJsOnChildren = h.EvalJsOnChildren
|
||||||
var EvalJsOnSibling = h.EvalJsOnSibling
|
var EvalJsOnSibling = h.EvalJsOnSibling
|
||||||
|
|
@ -23,6 +26,8 @@ var RemoveClassOnSibling = h.RemoveClassOnSibling
|
||||||
var Remove = h.Remove
|
var Remove = h.Remove
|
||||||
var PreventDefault = h.PreventDefault
|
var PreventDefault = h.PreventDefault
|
||||||
var EvalJs = h.EvalJs
|
var EvalJs = h.EvalJs
|
||||||
|
var EvalCommands = h.EvalCommands
|
||||||
|
var EvalCommandsOnSelector = h.EvalCommandsOnSelector
|
||||||
var ConsoleLog = h.ConsoleLog
|
var ConsoleLog = h.ConsoleLog
|
||||||
var SetValue = h.SetValue
|
var SetValue = h.SetValue
|
||||||
var SubmitFormOnEnter = h.SubmitFormOnEnter
|
var SubmitFormOnEnter = h.SubmitFormOnEnter
|
||||||
|
|
@ -36,3 +41,7 @@ var GetWithQs = h.GetWithQs
|
||||||
var PostWithQs = h.PostWithQs
|
var PostWithQs = h.PostWithQs
|
||||||
var ToggleClass = h.ToggleClass
|
var ToggleClass = h.ToggleClass
|
||||||
var ToggleClassOnElement = h.ToggleClassOnElement
|
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,
|
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{
|
var examples = []Snippet{
|
||||||
FormWithLoadingStateSnippet,
|
FormWithLoadingStateSnippet,
|
||||||
ClickToEditSnippet,
|
ClickToEditSnippet,
|
||||||
|
|
||||||
|
JsSetTextOnClick,
|
||||||
|
JsHideChildrenOnClick,
|
||||||
|
|
||||||
UserAuthSnippet,
|
UserAuthSnippet,
|
||||||
ChatSnippet,
|
ChatSnippet,
|
||||||
HackerNewsSnippet,
|
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