Extending Bulma-Gomponents

Here are some developer notes useful when there is a need to extend Bulma-Gomponents.

Implementing icon element

If you need to provide new icon elements to Bulma-Gomponents functions, you must implement the b.IconElem interface. This interface is implemented by the elements returned by b.Icon and fa.Icon.

Implementing classes and styles

If you need to provide new classes and/or styles to Bulma-Gomponents functions, the following possibilities arise:

  • Create a simple constant or variable of type e.Class - for instance: const myClass = e.Class("my-class")
  • Create a simple constant or variable of type b.ResponsiveClass if this class accepts the responsive suffixes
  • Implement the e.Classer interface to return a single class
  • Implement the e.Classeser interface to return multiple classes
  • Provide e.Styles to generate additional styles

Implementing e.Element

If you need to create new components for Bulma-Gomponents, you may prefer following one of these examples:

func MyElement(children ...any) e.Element {
	m := &myElement{e.Div(e.Class("my-element"))}
	m.With(children...)
	return m
}

type myElement struct {
	e.Element
}

func (m *myElement) With(children ...any) e.Element {
	for _, c := range children {
		switch c := c.(type) {
		case b.Color:
			m.Element.With(c.Background())
		case []any:
			m.With(c...)
		default:
			m.Element.With(c)
		}
	}
	return m
}

func (m *myElement) Clone() e.Element {
	return &myElement{m.Element.Clone()}
}
type weirdOption string

func MyWeirdElement(children ...any) e.Element {
	other := e.Span()
	m := &myWeirdElement{
		container: e.Div(other),
		other: other,
	}
	m.With(children...)
	return m
}

type myWeirdElement struct {
	container e.Element
	other e.Element

	weirdOption weirdOption

	rendered sync.Once
}

func (m *myWeirdElement) With(children ...any) e.Element {
	for _, c := range children {
		switch c := c.(type) {
		case e.Class, e.Classer, e.Classeser, e.Styles:
			// Apply classes and styles to content
			m.container.With(c)
		case weirdOption:
			b.weirdOption = weirdOption
		case e.Element:
			// Add element to content
			m.container.With(c)
		case gomponents.Node:
			if b.IsAttribute(c) {
				// Apply gomponents attribute nodes to content
				m.container.With(c)
			} else {
				// Apply other gomponents nodes (ie. elements) to other content
				m.other.With(c)
			}
		case []any:
			m.With(c...)
		default:
			// Apply all other children to some other content
			m.other.With(c)
		}
	}

	return m
}

func (m *myWeirdElement) Render(w io.Writer) error {
	m.rendered.Do(func() {
		if m.weirdOption != "" {
			doSomethingWeird(m.container, m.other)
		}
	})

	return m.container.Render(w)
}

func (m *myWeirdElement) Clone() e.Element {
	return &myWeirdElement{
		container: m.container.Clone(),
		other: m.other.Clone(),

		weirdOption: m.weirdOption,
	}
}