diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f8685b..49dc0c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ - `hatmill.Attrib.Value` is now an `fmt.Stringer`. - Attribute code generation mostly uses interfaces instead of being so ad-hoc. +- Empty `hatmill.Attrib`s are not rendered at all in HTML. +- Generated boolean attribute helper function now have a boolean parameter rather than depending on the presence of the call. ### Removed diff --git a/attribute/coords_test.go b/attribute/coords_test.go index 80f96fb..5aad7bb 100644 --- a/attribute/coords_test.go +++ b/attribute/coords_test.go @@ -20,11 +20,11 @@ func TestCoordsList(t *testing.T) { {name: "zero", f: 0, s: "0"}, {name: "integral", f: 45, s: "45"}, {name: "positive fractional, small positive exponent", f: 12.55, s: "12.55"}, - {name: "positive fractional, large positive exponent", f: 1.55E+12, s: "1.55E+12"}, - {name: "positive fractional, large negative exponent", f: 1.55E-12, s: "1.55E-12"}, + {name: "positive fractional, large positive exponent", f: 1.55e+12, s: "1.55E+12"}, + {name: "positive fractional, large negative exponent", f: 1.55e-12, s: "1.55E-12"}, {name: "negative fractional, small positive exponent", f: -12.55, s: "-12.55"}, - {name: "negative fractional, large positive exponent", f: -1.55E+12, s: "-1.55E+12"}, - {name: "negative fractional, large negative exponent", f: -1.55E-12, s: "-1.55E-12"}, + {name: "negative fractional, large positive exponent", f: -1.55e+12, s: "-1.55E+12"}, + {name: "negative fractional, large negative exponent", f: -1.55e-12, s: "-1.55E-12"}, } { t.Run(testcase.name, func(t *testing.T) { expectEqualStrings(t, attribute.CoordsList{testcase.f}.String(), testcase.s) diff --git a/attribute/generated.go b/attribute/generated.go index 8e0c05b..c94506c 100644 --- a/attribute/generated.go +++ b/attribute/generated.go @@ -38,7 +38,10 @@ func Alt(value string) hatmill.Attrib { } // Async creates a "async" attribute -func Async() hatmill.Attrib { +func Async(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "async", } @@ -61,14 +64,20 @@ func Autocomplete(value string) hatmill.Attrib { } // Autofocus creates a "autofocus" attribute -func Autofocus() hatmill.Attrib { +func Autofocus(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "autofocus", } } // Autoplay creates a "autoplay" attribute -func Autoplay() hatmill.Attrib { +func Autoplay(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "autoplay", } @@ -83,7 +92,10 @@ func Charset(value string) hatmill.Attrib { } // Checked creates a "checked" attribute -func Checked() hatmill.Attrib { +func Checked(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "checked", } @@ -146,7 +158,10 @@ func Contextmenu(value string) hatmill.Attrib { } // Controls creates a "controls" attribute -func Controls() hatmill.Attrib { +func Controls(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "controls", } @@ -177,14 +192,20 @@ func Decoding(value string) hatmill.Attrib { } // Default creates a "default" attribute -func Default() hatmill.Attrib { +func Default(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "default", } } // Defer creates a "defer" attribute -func Defer() hatmill.Attrib { +func Defer(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "defer", } @@ -207,7 +228,10 @@ func Dirname(value string) hatmill.Attrib { } // Disabled creates a "disabled" attribute -func Disabled() hatmill.Attrib { +func Disabled(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "disabled", } @@ -286,7 +310,10 @@ func Height(value int) hatmill.Attrib { } // Hidden creates a "hidden" attribute -func Hidden() hatmill.Attrib { +func Hidden(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "hidden", } @@ -333,7 +360,10 @@ func Id(value string) hatmill.Attrib { } // Ismap creates a "ismap" attribute -func Ismap() hatmill.Attrib { +func Ismap(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "ismap", } @@ -380,7 +410,10 @@ func List(value string) hatmill.Attrib { } // Loop creates a "loop" attribute -func Loop() hatmill.Attrib { +func Loop(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "loop", } @@ -443,14 +476,20 @@ func Minlength(value int) hatmill.Attrib { } // Multiple creates a "multiple" attribute -func Multiple() hatmill.Attrib { +func Multiple(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "multiple", } } // Muted creates a "muted" attribute -func Muted() hatmill.Attrib { +func Muted(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "muted", } @@ -465,14 +504,20 @@ func Name(value string) hatmill.Attrib { } // Novalidate creates a "novalidate" attribute -func Novalidate() hatmill.Attrib { +func Novalidate(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "novalidate", } } // Open creates a "open" attribute -func Open() hatmill.Attrib { +func Open(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "open", } @@ -527,7 +572,10 @@ func Preload(value string) hatmill.Attrib { } // Readonly creates a "readonly" attribute -func Readonly() hatmill.Attrib { +func Readonly(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "readonly", } @@ -550,14 +598,20 @@ func Rel(value ...string) hatmill.Attrib { } // Required creates a "required" attribute -func Required() hatmill.Attrib { +func Required(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "required", } } // Reversed creates a "reversed" attribute -func Reversed() hatmill.Attrib { +func Reversed(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "reversed", } @@ -596,7 +650,10 @@ func Scope(value string) hatmill.Attrib { } // Selected creates a "selected" attribute -func Selected() hatmill.Attrib { +func Selected(b bool) hatmill.Attrib { + if !b { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "selected", } diff --git a/attribute/generated_test.go b/attribute/generated_test.go index a8a49dd..6518918 100644 --- a/attribute/generated_test.go +++ b/attribute/generated_test.go @@ -1,12 +1,13 @@ package attribute_test import ( - "gitlab.codemonkeysoftware.net/b/hatmill/attribute" "testing" + + "gitlab.codemonkeysoftware.net/b/hatmill/attribute" ) func TestBool(t *testing.T) { - attrib := attribute.Disabled() + attrib := attribute.Disabled(true) if attrib.Value != nil { t.Fatalf("expected attribute value to be nil, but got %#v", attrib.Value) } diff --git a/attribute/value_types_test.go b/attribute/value_types_test.go index 38b46de..1a70964 100644 --- a/attribute/value_types_test.go +++ b/attribute/value_types_test.go @@ -44,11 +44,11 @@ func TestFloat(t *testing.T) { {name: "zero", f: 0, s: "0"}, {name: "integral", f: 45, s: "45"}, {name: "positive fractional, small positive exponent", f: 12.55, s: "12.55"}, - {name: "positive fractional, large positive exponent", f: 1.55E+12, s: "1.55E+12"}, - {name: "positive fractional, large negative exponent", f: 1.55E-12, s: "1.55E-12"}, + {name: "positive fractional, large positive exponent", f: 1.55e+12, s: "1.55E+12"}, + {name: "positive fractional, large negative exponent", f: 1.55e-12, s: "1.55E-12"}, {name: "negative fractional, small positive exponent", f: -12.55, s: "-12.55"}, - {name: "negative fractional, large positive exponent", f: -1.55E+12, s: "-1.55E+12"}, - {name: "negative fractional, large negative exponent", f: -1.55E-12, s: "-1.55E-12"}, + {name: "negative fractional, large positive exponent", f: -1.55e+12, s: "-1.55E+12"}, + {name: "negative fractional, large negative exponent", f: -1.55e-12, s: "-1.55E-12"}, } { t.Run(testcase.name, func(t *testing.T) { expectEqualStrings(t, testcase.f.String(), testcase.s) diff --git a/go.mod b/go.mod index 71e73bb..9fd2728 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module gitlab.codemonkeysoftware.net/b/hatmill +go 1.14 + require github.com/leanovate/gopter v0.2.4 diff --git a/hatmill.go b/hatmill.go index 6502b54..54c5cbb 100644 --- a/hatmill.go +++ b/hatmill.go @@ -36,11 +36,18 @@ type Attrib struct { Value fmt.Stringer } +func (a Attrib) Empty() bool { + return a == Attrib{} +} + // WriteTo writes a to w as an HTML attribute in the form key="value", or // simply key if value is empty. Special characters in value are replaced with // HTML entities. It returns the number of bytes written and any // error encountered. func (a Attrib) WriteTo(w io.Writer) (n int64, err error) { + if a.Empty() { + return 0, nil + } err = writeStringsTo(w, &n, a.Key) if err != nil { return @@ -73,6 +80,11 @@ func (e VoidElement) WriteTo(w io.Writer) (n int64, err error) { } for _, attrib := range e.Attribs { + if attrib.Empty() { + // attrib.WriteTo already does nothing in this case, but + // let's not clutter our tags with extra spaces. + continue + } err = writeStringsTo(w, &n, " ") if err != nil { return diff --git a/hatmill_test.go b/hatmill_test.go index 0931ebe..aeabfa9 100644 --- a/hatmill_test.go +++ b/hatmill_test.go @@ -242,9 +242,9 @@ func Example() { he.Body()( he.Img(ha.Src("./photo.jpg"), ha.Contenteditable(true)), hatmill.Text(userInput), - he.Div(ha.Disabled(), ha.CustomData("coolness", "awesome"))(), + he.Div(ha.Disabled(true), ha.CustomData("coolness", "awesome"))(), he.Textarea(ha.Rows(25))(), - he.Meter(ha.Min(-1.3), ha.Max(5.5E12))(), + he.Meter(ha.Min(-1.3), ha.Max(5.5e12))(), ), ) hatmill.WriteDocument(os.Stdout, document) diff --git a/internal/codegen/codegen.go b/internal/codegen/codegen.go index 3f6f54c..3040cbd 100644 --- a/internal/codegen/codegen.go +++ b/internal/codegen/codegen.go @@ -40,7 +40,10 @@ func simpleTemplate(paramType, toType string) AttribTemplate { func boolTemplate(identifier, name string) string { return fmt.Sprintf( - `func %s() hatmill.Attrib { + `func %s(showAttrib bool) hatmill.Attrib { + if !showAttrib { + return hatmill.Attrib{} + } return hatmill.Attrib{ Key: "%s", }