Created
November 8, 2019 17:47
-
-
Save iansmith/528c10f2d89e2b87bdf782a445216e09 to your computer and use it in GitHub Desktop.
one file for a HTML generation tool from go -- also has some support for bootstrap 4 classes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// +build wasm | |
// | |
// The value returned by a tag name function is actually a function (closure) that can be | |
// instantiated by invoking it with a particular document. | |
// | |
// The params to a tag name function can be other tag name functions, Text() to | |
// set innerHtml, Attribute{attrName:"value"}, Events{eventName:handlerFunc}, | |
// or Clazz (css classes) names. If you pass something else, the tag name | |
// functions panic. | |
// | |
// Example to create a bootstrap alert div, appropriately decorated, and invoke | |
// the instantiation of it on a doc. | |
// | |
// <div class="alert alert-warning" role="alert" > | |
// This is a primary alert—check it out! | |
// <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | |
// <span aria-hidden="true">×</span> | |
// </button> | |
// </div> | |
// func BootstrapAlert(doc dom.Document, alertText string) *dom.BasicHTMLElement { | |
// | |
// //you can also do event handlers with Events{Click:functionName} | |
// return Div(Alert, AlertWarning, AlertDismissable, Fade, Show, Attributes{Role: "alert"}, Text(alertText), | |
// Button(Close, Attributes{Type: "button", ArialLabel: "Close", DataDismiss: "alert"}, | |
// Span(Attributes{AriaHidden: "true"}, Text("\u00D7")), | |
// ), | |
// )(doc) | |
// | |
// } | |
package web | |
import ( | |
"fmt" | |
"log" | |
"strings" | |
"syscall/js" | |
dom "honnef.co/go/js/dom/v2" | |
) | |
type Frob interface { | |
Apply(*dom.BasicHTMLElement) | |
} | |
type Elem func(dom.Document) *dom.BasicHTMLElement | |
func (e Elem) Apply(*dom.BasicHTMLElement) { | |
panic("should not call Apply on an Element!") | |
} | |
type AttributeName string | |
type Attributes map[AttributeName]string | |
func (a Attributes) Apply(elem *dom.BasicHTMLElement) { | |
for k, v := range a { | |
elem.SetAttribute(string(k), v) | |
} | |
} | |
type EventName string | |
type Events map[EventName]interface{} | |
const CaptureSuffix = "-capture" | |
func (e Events) Apply(elem *dom.BasicHTMLElement) { | |
outbound := make(map[EventName]js.Func) | |
for k, v := range e { | |
fn, ok := v.(func(dom.Event)) | |
if !ok { | |
log.Printf("function of %s cannot be converted to func(dom.Event): %T", v) | |
return | |
} | |
s := string(k) | |
capture := false | |
if strings.HasSuffix(strings.ToLower(s), CaptureSuffix) { | |
s = strings.TrimSuffix(s, CaptureSuffix) | |
capture = true | |
} | |
f := elem.AddEventListener(s, capture, fn) | |
outbound[k] = f | |
} | |
for k, newFunc := range outbound { | |
e[k] = newFunc | |
} | |
} | |
type Clazz string | |
func (c Clazz) Apply(elem *dom.BasicHTMLElement) { | |
classes := elem.Class() | |
if !classes.Contains(string(c)) { | |
classes.Add(string(c)) | |
} | |
} | |
type Text string | |
func (t Text) Apply(elem *dom.BasicHTMLElement) { | |
elem.SetInnerHTML(string(t)) | |
} | |
// Div is <div> tag. Frobs can be any Frob but anything else panics. | |
func Div(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
div := doc.CreateElement("div").(*dom.HTMLDivElement) | |
e := div.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
func processFrobs(doc dom.Document, e *dom.BasicHTMLElement, frobs ...Frob) *dom.BasicHTMLElement { | |
for _, f := range frobs { | |
if f == nil { | |
continue | |
} | |
child, ok := f.(Elem) | |
if ok { | |
if child == nil { | |
continue | |
} | |
e.AppendChild(child(doc)) | |
} else { | |
f.Apply(e) | |
} | |
} | |
return e | |
} | |
// Button is <button> tag. Frobs can be any Frob but anything else panics. | |
func Button(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
button := doc.CreateElement("button").(*dom.HTMLButtonElement) | |
e := button.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// Span is <span> tag. Frobs can be any Frob but anything else panics. | |
func Span(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
span := doc.CreateElement("span").(*dom.HTMLSpanElement) | |
e := span.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// Form is <form> tag. Frobs can be any Frob but anything else panics. | |
func Form(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
span := doc.CreateElement("form").(*dom.HTMLFormElement) | |
e := span.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// TextArea is <textarea> tag. Frobs can be any Frob but anything else panics. | |
func TextArea(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
span := doc.CreateElement("textarea").(*dom.HTMLTextAreaElement) | |
e := span.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// P is <p> tag. Frobs can be any Frob but anything else panics. | |
func P(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
p := doc.CreateElement("p").(*dom.HTMLParagraphElement) | |
e := p.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// I is <i> tag. Frobs can be any Frob but anything else panics. | |
func I(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
i := doc.CreateElement("i").(*dom.BasicHTMLElement) | |
return processFrobs(doc, i, frobs...) | |
} | |
} | |
// A is <a> tag. Frobs can be any Frob but anything else panics. | |
func A(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
a := doc.CreateElement("a").(*dom.HTMLAnchorElement) | |
e := a.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// H6 is <h6> tag. Frobs can be any Frob but anything else panics. | |
func H6(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
a := doc.CreateElement("h6").(*dom.HTMLHeadingElement) | |
e := a.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// Ul is <ul> tag. Frobs can be any Frob but anything else panics. | |
func Ul(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
ul := doc.CreateElement("ul").(*dom.HTMLUListElement) | |
e := ul.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// Li is <li> tag. Frobs can be any Frob but anything else panics. | |
func Li(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
li := doc.CreateElement("li").(*dom.HTMLLIElement) | |
e := li.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// Img is <img> tag. Frobs can be any Frob but anything else panics. | |
func Img(frobs ...Frob) Elem { | |
return func(doc dom.Document) *dom.BasicHTMLElement { | |
img := doc.CreateElement("img").(*dom.HTMLImageElement) | |
e := img.BasicHTMLElement | |
return processFrobs(doc, e, frobs...) | |
} | |
} | |
// | |
// Useful stuff from bootstrap | |
// | |
const Alt = AttributeName("alt") | |
const DataDismiss = AttributeName("data-dismiss") | |
const DataTarget = AttributeName("data-target") | |
const DataToggle = AttributeName("data-toggle") | |
const Draggable = AttributeName("draggable") | |
const Height = AttributeName("height") | |
const Href = AttributeName("href") | |
const ID = AttributeName("id") | |
const OnClick = AttributeName("onclick") | |
const Placeholder = AttributeName("placeholder") | |
const Role = AttributeName("role") | |
const Rows = AttributeName("rows") | |
const Style = AttributeName("style") | |
const Src = AttributeName("src") | |
const TabIndex = AttributeName("tabindex") | |
const Title = AttributeName("title") | |
const Type = AttributeName("type") | |
const Width = AttributeName("width") | |
const AriaHidden = AttributeName("aria-hidden") | |
const AriaHasPopup = AttributeName("aria-haspopup") | |
const AriaExpanded = AttributeName("aria-expanded") | |
const ArialLabel = AttributeName("aria-label") | |
const Alert = Clazz("alert") | |
const AlertDismissable = Clazz("alert-dismissable") | |
const AlertWarning = Clazz("alert-warning") | |
const AlignItemsCenter = Clazz("align-items-center") | |
const BorderPrimary = Clazz("border-primary") | |
const Btn = Clazz("btn") | |
const BtnLink = Clazz("btn-link") | |
const BtnSm = Clazz("btn-sm") | |
const BtnOptions = Clazz("btn-options") | |
const Close = Clazz("close") | |
const Dflex = Clazz("d-flex") | |
const Dnone = Clazz("d-none") | |
const Dropdown = Clazz("dropdown") | |
const DropdownItem = Clazz("dropdown-item") | |
const DropdownMenu = Clazz("dropdown-menu") | |
const DropdownMenuRight = Clazz("dropdown-menu-right") | |
const Fade = Clazz("fade") | |
const FormControl = Clazz("form-control") | |
const JustifyContentBetween = Clazz("justify-content-between") | |
const H1Size = Clazz("h1") | |
const H6Size = Clazz("h6") | |
const ListGroup = Clazz("list-group") | |
const ListGroupFlush = Clazz("list-group-flush") | |
const ListGroupItem = Clazz("list-group-item") | |
const MaterialIcons = Clazz("material-icons") | |
const NoMargin = Clazz("m-0") | |
const NoPadding = Clazz("p-0") | |
const Row = Clazz("row") | |
const Small = Clazz("small") | |
const Show = Clazz("show") | |
const TextCenter = Clazz("text-center") | |
const TextDanger = Clazz("text-danger") | |
const TextPrimary = Clazz("text-primary") | |
const TextSmall = Clazz("text-small") | |
const TextSecondary = Clazz("text-secondary") | |
func Margin(n int) Clazz { | |
return Clazz(fmt.Sprintf("m-%d", n)) | |
} | |
func Padding(n int) Clazz { | |
return Clazz(fmt.Sprintf("p-%d", n)) | |
} | |
func Offset(n int) Clazz { | |
return Clazz(fmt.Sprintf("offset-%d", n)) | |
} | |
func Col(n int) Clazz { | |
return Clazz(fmt.Sprintf("col-%d", n)) | |
} | |
const Click = EventName("click") | |
const ClickCapture = EventName("click" + CaptureSuffix) | |
const DoubleClick = EventName("dblclick") | |
const DoubleClickCapture = EventName("dblclick" + CaptureSuffix) | |
const MouseDown = EventName("mousedown") | |
const MouseUp = EventName("mouseup") | |
const MouseDownCapture = EventName("mousedown" + CaptureSuffix) | |
const MouseUpCapture = EventName("mouseup" + CaptureSuffix) | |
func BootstrapAlert(doc dom.Document, alertText string) *dom.BasicHTMLElement { | |
//you can also do event handlers with Events{Click:functionName} | |
return Div(Alert, AlertWarning, AlertDismissable, Fade, Show, Attributes{Role: "alert"}, Text(alertText), | |
Button(Close, Attributes{Type: "button", ArialLabel: "Close", DataDismiss: "alert"}, | |
Span(Attributes{AriaHidden: "true"}, Text("\u00D7")), | |
), | |
)(doc) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment