2024-09-20 01:24:44 +00:00
|
|
|
package h
|
|
|
|
|
|
2024-09-21 16:52:56 +00:00
|
|
|
import (
|
|
|
|
|
"fmt"
|
2024-10-22 13:32:17 +00:00
|
|
|
"strings"
|
|
|
|
|
|
2024-09-21 16:52:56 +00:00
|
|
|
"github.com/maddalax/htmgo/framework/hx"
|
2024-09-30 17:39:48 +00:00
|
|
|
"github.com/maddalax/htmgo/framework/internal/datastructure"
|
2024-09-21 16:52:56 +00:00
|
|
|
)
|
2024-09-20 01:40:17 +00:00
|
|
|
|
2024-09-30 17:39:48 +00:00
|
|
|
type AttributeMap = map[string]any
|
|
|
|
|
|
|
|
|
|
type AttributeMapOrdered struct {
|
|
|
|
|
data *datastructure.OrderedMap[string, string]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *AttributeMapOrdered) Set(key string, value any) {
|
|
|
|
|
switch v := value.(type) {
|
|
|
|
|
case string:
|
|
|
|
|
m.data.Set(key, v)
|
2024-09-30 18:01:51 +00:00
|
|
|
case *AttributeMap:
|
|
|
|
|
for k, v2 := range *v {
|
|
|
|
|
m.Set(k, v2)
|
|
|
|
|
}
|
2024-09-30 17:39:48 +00:00
|
|
|
case *AttributeMapOrdered:
|
2024-09-30 18:01:51 +00:00
|
|
|
v.Each(func(k string, v2 string) {
|
|
|
|
|
m.Set(k, v2)
|
2024-09-30 17:39:48 +00:00
|
|
|
})
|
|
|
|
|
case *AttributeR:
|
|
|
|
|
m.data.Set(v.Name, v.Value)
|
|
|
|
|
default:
|
|
|
|
|
m.data.Set(key, fmt.Sprintf("%v", value))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-30 17:47:10 +00:00
|
|
|
func (m *AttributeMapOrdered) Each(cb func(key string, value string)) {
|
2024-09-30 17:39:48 +00:00
|
|
|
m.data.Each(func(key string, value string) {
|
|
|
|
|
cb(key, value)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *AttributeMapOrdered) Entries() []datastructure.MapEntry[string, string] {
|
|
|
|
|
return m.data.Entries()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewAttributeMap(pairs ...string) *AttributeMapOrdered {
|
|
|
|
|
m := datastructure.NewOrderedMap[string, string]()
|
|
|
|
|
if len(pairs)%2 == 0 {
|
|
|
|
|
for i := 0; i < len(pairs); i++ {
|
|
|
|
|
m.Set(pairs[i], pairs[i+1])
|
|
|
|
|
i++
|
2024-09-20 01:40:17 +00:00
|
|
|
}
|
2024-09-20 01:24:44 +00:00
|
|
|
}
|
2024-09-30 17:39:48 +00:00
|
|
|
return &AttributeMapOrdered{data: m}
|
2024-09-20 01:24:44 +00:00
|
|
|
}
|
2024-09-21 16:52:56 +00:00
|
|
|
|
2024-10-02 03:26:03 +00:00
|
|
|
func NoSwap() *AttributeR {
|
|
|
|
|
return Attribute("hx-swap", "none")
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 15:10:00 +00:00
|
|
|
func Attribute(key string, value string) *AttributeR {
|
|
|
|
|
return &AttributeR{
|
|
|
|
|
Name: key,
|
|
|
|
|
Value: value,
|
|
|
|
|
}
|
2024-09-21 16:52:56 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-30 17:39:48 +00:00
|
|
|
func AttributeList(children ...*AttributeR) *AttributeMapOrdered {
|
|
|
|
|
m := NewAttributeMap()
|
|
|
|
|
for _, c := range children {
|
|
|
|
|
m.Set(c.Name, c.Value)
|
2024-09-21 16:52:56 +00:00
|
|
|
}
|
2024-09-30 17:39:48 +00:00
|
|
|
return m
|
2024-09-21 16:52:56 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-30 17:39:48 +00:00
|
|
|
func Attributes(attributes *AttributeMap) *AttributeMapOrdered {
|
|
|
|
|
m := NewAttributeMap()
|
|
|
|
|
for k, v := range *attributes {
|
|
|
|
|
m.Set(k, v)
|
|
|
|
|
}
|
|
|
|
|
return m
|
2024-09-21 16:52:56 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-30 17:39:48 +00:00
|
|
|
func AttributePairs(pairs ...string) *AttributeMapOrdered {
|
|
|
|
|
return NewAttributeMap(pairs...)
|
2024-09-21 16:52:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Checked() Ren {
|
2024-09-29 06:05:22 +00:00
|
|
|
return Attribute("checked", "")
|
2024-09-21 16:52:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Id(value string) Ren {
|
2024-10-22 13:32:17 +00:00
|
|
|
value = strings.TrimPrefix(value, "#")
|
2024-09-21 16:52:56 +00:00
|
|
|
return Attribute("id", value)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-29 06:05:22 +00:00
|
|
|
func Disabled() *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return Attribute("disabled", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HxTarget(target string) Ren {
|
|
|
|
|
return Attribute(hx.TargetAttr, target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Name(name string) Ren {
|
|
|
|
|
return Attribute("name", name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HxConfirm(message string) Ren {
|
|
|
|
|
return Attribute(hx.ConfirmAttr, message)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HxInclude https://htmx.org/attributes/hx-include/
|
|
|
|
|
func HxInclude(selector string) Ren {
|
|
|
|
|
return Attribute(hx.IncludeAttr, selector)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 15:10:00 +00:00
|
|
|
func HxIndicator(tag string) *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return Attribute(hx.IndicatorAttr, tag)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// TriggerChildren Adds the hx-extension="trigger-children" to an element
|
|
|
|
|
// See https://htmgo.dev/docs#htmx-extensions-trigger-children
|
2024-09-30 21:05:06 +00:00
|
|
|
func TriggerChildren() *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return HxExtension("trigger-children")
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxTriggerString Adds a hx-trigger to an element based on a string of triggers
|
2024-09-27 15:10:00 +00:00
|
|
|
func HxTriggerString(triggers ...string) *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
trigger := hx.NewStringTrigger(strings.Join(triggers, ", "))
|
|
|
|
|
return Attribute(hx.TriggerAttr, trigger.ToString())
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxTrigger Adds a hx-trigger to an element
|
2024-09-27 15:10:00 +00:00
|
|
|
func HxTrigger(opts ...hx.TriggerEvent) *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return Attribute(hx.TriggerAttr, hx.NewTrigger(opts...).ToString())
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxTriggerClick Adds a hx-trigger="click" to an element
|
2024-09-27 15:10:00 +00:00
|
|
|
func HxTriggerClick(opts ...hx.Modifier) *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return HxTrigger(hx.OnClick(opts...))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxExtension Adds a hx-ext to an element
|
2024-09-30 21:05:06 +00:00
|
|
|
func HxExtension(value string) *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return Attribute(hx.ExtAttr, value)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// HxExtensions Adds multiple hx-ext to an element, separated by commas
|
2024-09-30 21:05:06 +00:00
|
|
|
func HxExtensions(value ...string) Ren {
|
|
|
|
|
return Attribute(hx.ExtAttr, strings.Join(value, ","))
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-30 21:32:12 +00:00
|
|
|
func JoinExtensions(attrs ...*AttributeR) Ren {
|
|
|
|
|
return JoinAttributes(", ", attrs...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// JoinAttributes joins multiple attributes into a single attribute string based on a separator
|
|
|
|
|
// Example: JoinAttributes(", ", Attribute("hx-extension", "one"), Attribute("hx-extension", "two")) = hx-extension="one,two"
|
2024-09-30 21:05:06 +00:00
|
|
|
func JoinAttributes(sep string, attrs ...*AttributeR) *AttributeR {
|
|
|
|
|
values := make([]string, 0, len(attrs))
|
|
|
|
|
for _, a := range attrs {
|
|
|
|
|
values = append(values, a.Value)
|
|
|
|
|
}
|
|
|
|
|
return Attribute(attrs[0].Name, strings.Join(values, sep))
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-21 16:52:56 +00:00
|
|
|
func Href(path string) Ren {
|
|
|
|
|
return Attribute("href", path)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 02:15:04 +00:00
|
|
|
func Target(target string) Ren {
|
|
|
|
|
return Attribute("target", target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func D(value string) Ren {
|
|
|
|
|
return Attribute("d", value)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 14:46:45 +00:00
|
|
|
func Alt(value string) Ren {
|
|
|
|
|
return Attribute("alt", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func For(value string) Ren {
|
|
|
|
|
return Attribute("for", value)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-21 16:52:56 +00:00
|
|
|
func Type(name string) Ren {
|
|
|
|
|
return Attribute("type", name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Placeholder(placeholder string) Ren {
|
|
|
|
|
return Attribute("placeholder", placeholder)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Hidden() Ren {
|
|
|
|
|
return Attribute("style", "display:none")
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-27 15:10:00 +00:00
|
|
|
func Class(value ...string) *AttributeR {
|
2024-09-21 16:52:56 +00:00
|
|
|
return Attribute("class", MergeClasses(value...))
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 03:01:04 +00:00
|
|
|
// ClassF is a helper function to create a class attribute with the given format string and arguments
|
2024-10-22 13:32:17 +00:00
|
|
|
func ClassF(format string, args ...interface{}) *AttributeR {
|
|
|
|
|
atr := fmt.Sprintf(format, args...)
|
|
|
|
|
return Attribute("class", atr)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// ClassX conditionally renders a class based on a map of class names and boolean values
|
|
|
|
|
// value is any non-conditional class name you'd like to add
|
|
|
|
|
// m is a map of class names and boolean values
|
2024-09-21 16:52:56 +00:00
|
|
|
func ClassX(value string, m ClassMap) Ren {
|
|
|
|
|
builder := strings.Builder{}
|
|
|
|
|
builder.WriteString(value)
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
for k, v := range m {
|
|
|
|
|
if v {
|
|
|
|
|
builder.WriteString(k)
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Class(builder.String())
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-26 02:59:17 +00:00
|
|
|
// MergeClasses merges multiple classes into a single class string
|
2024-09-21 16:52:56 +00:00
|
|
|
func MergeClasses(classes ...string) string {
|
|
|
|
|
if len(classes) == 1 {
|
|
|
|
|
return classes[0]
|
|
|
|
|
}
|
|
|
|
|
builder := strings.Builder{}
|
|
|
|
|
for _, s := range classes {
|
|
|
|
|
builder.WriteString(s)
|
|
|
|
|
builder.WriteString(" ")
|
|
|
|
|
}
|
|
|
|
|
return builder.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Boost() Ren {
|
|
|
|
|
return Attribute(hx.BoostAttr, "true")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func IfQueryParam(key string, node *Element) Ren {
|
|
|
|
|
return Fragment(Attribute("hx-if-qp:"+key, "true"), node)
|
|
|
|
|
}
|
2024-09-29 06:05:22 +00:00
|
|
|
|
|
|
|
|
func ReadOnly() *AttributeR {
|
|
|
|
|
return Attribute("readonly", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Required() *AttributeR {
|
|
|
|
|
return Attribute("required", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Multiple() *AttributeR {
|
|
|
|
|
return Attribute("multiple", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Selected() *AttributeR {
|
|
|
|
|
return Attribute("selected", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MaxLength(value int) *AttributeR {
|
|
|
|
|
return Attribute("maxlength", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func MinLength(value int) *AttributeR {
|
|
|
|
|
return Attribute("minlength", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Size(value int) *AttributeR {
|
|
|
|
|
return Attribute("size", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Width(value int) *AttributeR {
|
|
|
|
|
return Attribute("width", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Height(value int) *AttributeR {
|
|
|
|
|
return Attribute("height", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Download(value bool) *AttributeR {
|
|
|
|
|
return Attribute("download", fmt.Sprintf("%t", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Rel(value string) *AttributeR {
|
|
|
|
|
return Attribute("rel", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Pattern(value string) *AttributeR {
|
|
|
|
|
return Attribute("pattern", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Action(value string) *AttributeR {
|
|
|
|
|
return Attribute("action", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Method(value string) *AttributeR {
|
|
|
|
|
return Attribute("method", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Enctype(value string) *AttributeR {
|
|
|
|
|
return Attribute("enctype", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AutoComplete(value string) *AttributeR {
|
|
|
|
|
return Attribute("autocomplete", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AutoFocus() *AttributeR {
|
|
|
|
|
return Attribute("autofocus", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NoValidate() *AttributeR {
|
|
|
|
|
return Attribute("novalidate", "")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Step(value string) *AttributeR {
|
|
|
|
|
return Attribute("step", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Max(value string) *AttributeR {
|
|
|
|
|
return Attribute("max", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Min(value string) *AttributeR {
|
|
|
|
|
return Attribute("min", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Cols(value int) *AttributeR {
|
|
|
|
|
return Attribute("cols", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Rows(value int) *AttributeR {
|
|
|
|
|
return Attribute("rows", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Wrap(value string) *AttributeR {
|
|
|
|
|
return Attribute("wrap", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Role(value string) *AttributeR {
|
|
|
|
|
return Attribute("role", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AriaLabel(value string) *AttributeR {
|
|
|
|
|
return Attribute("aria-label", value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func AriaHidden(value bool) *AttributeR {
|
|
|
|
|
return Attribute("aria-hidden", fmt.Sprintf("%t", value))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TabIndex(value int) *AttributeR {
|
|
|
|
|
return Attribute("tabindex", fmt.Sprintf("%d", value))
|
|
|
|
|
}
|