Code Monkey home page Code Monkey logo

domui's Introduction

domui: DOM UI framework for Go

Features

  • Pure go code compiled to wasm
    • Utilizing Go's excellent support for static typing and dynamic reflection
    • Runs on any WebAssembly and DOM supported browsers, Electron, etc
  • Dependent reactive system for both view and state management
    • Conceptually unify view component and state management
    • No hooks, redux, recoil, mobx ...
  • Auto and efficient DOM tree updating
    • Based on virtual DOM diff and patch

Prerequisites

  • Go compiler 1.16 or newer
  • If you are not familiar with compiling and running WebAssembly program, please read the official wiki

Table of contents

package main

import (
	"github.com/reusee/domui"
	"syscall/js"
	"time"
)

// ergonomic aliases
var (
	Div = domui.Tag("div")
	T   = domui.Text
)

// A type to hold all definitions
type Def struct{}

// define the root UI element
func (_ Def) RootElement() domui.RootElement {
	// same to <div>Hello, world!</div>
	return Div(T("Hello, world!"))
}

func main() {
	domui.NewApp(
		// render on <div id="app">
		js.Global().Get("document").Call("getElementById", "app"),
		// pass Def's methods as definitions
		domui.Methods(new(Def))...,
	)
	// prevent from exiting
	time.Sleep(time.Hour * 24 * 365 * 100)
}

The definition of RootElement can be refactored to multiple dependent components.

package main

import (
	"github.com/reusee/domui"
	"syscall/js"
	"time"
)

var (
	Div = domui.Tag("div")
	T   = domui.Text
)

type (
	Def  struct{}
	Spec = domui.Spec
)

// A string-typed state
type Greetings string

// define Greetings
func (_ Def) Greetings() Greetings {
	return "Hello, world!"
}

// A UI element
type GreetingsElement Spec

// define GreetingsElement
func (_ Def) GreetingsElement(
	// use Greetings
	greetings Greetings,
) GreetingsElement {
	return Div(T("%s", greetings))
}

// The root UI element
func (_ Def) RootElement(
	// use GreetingsElement
	greetingsElem GreetingsElement,
) domui.RootElement {
	return Div(
		greetingsElem,
	)
}

func main() {
	domui.NewApp(
		js.Global().Get("document").Call("getElementById", "app"),
		domui.Methods(new(Def))...,
	)
	time.Sleep(time.Hour * 24 * 365 * 100)
}

Definitions can be updated. All affected definitions will be re-calculated recursively till the RootElement.

package main

import (
	"github.com/reusee/domui"
	"syscall/js"
	"time"
)

var (
	Div     = domui.Tag("div")
	T       = domui.Text
	OnClick = domui.On("click")
)

type (
	Def    struct{}
	Spec   = domui.Spec
	Update = domui.Update
)

type Greetings string

func (_ Def) Greetings() Greetings {
	return "Hello, world!"
}

type GreetingsElement Spec

func (_ Def) GreetingsElement(
	greetings Greetings,
) GreetingsElement {
	return Div(T("%s", greetings))
}

func (_ Def) RootElement(
	greetingsElem GreetingsElement,
	// use the Update function
	update Update,
) domui.RootElement {
	return Div(
		greetingsElem,

		// when clicked, do update
		OnClick(func() {
			// provide a new definition for Greetings
			update(func() Greetings {
				return "Hello, DomUI!"
			})
		}),
	)
}

func main() {
	domui.NewApp(
		js.Global().Get("document").Call("getElementById", "app"),
		domui.Methods(new(Def))...,
	)
	time.Sleep(time.Hour * 24 * 365 * 100)
}

The above programs demonstrated tag and event usages. Attributes, styles, classes can also be specified.

package main

import (
	"github.com/reusee/domui"
	"syscall/js"
	"time"
)

var (
	Div        = domui.Tag("div")
	Link       = domui.Tag("a")
	Ahref      = domui.Attr("href")
	Sfont_size = domui.Style("font-size")
	T          = domui.Text
	ID         = domui.ID
	Class      = domui.Class
)

type Def struct{}

func (_ Def) RootElement() domui.RootElement {
	return Div(
		Link(
			T("Hello, world!"),
			// id
			ID("link"),
			// class
			Class("link1", "link2"),
			// href attribute
			Ahref("http://github.com"),
			// font-size style
			Sfont_size("1.6rem"),
		),
	)
}

func main() {
	domui.NewApp(
		js.Global().Get("document").Call("getElementById", "app"),
		domui.Methods(new(Def))...,
	)
	time.Sleep(time.Hour * 24 * 365 * 100)
}

To make a reusable element, define it as a function.

package main

import (
	"fmt"
	"github.com/reusee/domui"
	"reflect"
	"strings"
	"syscall/js"
	"time"
)

var (
	Div     = domui.Tag("div")
	T       = domui.Text
	OnClick = domui.On("click")
)

type (
	Def    struct{}
	any    = interface{}
	Spec   = domui.Spec
	Update = domui.Update
	Specs  = domui.Specs
)

// Greetings with name parameter
type Greetings func(name any) Spec

func (_ Def) Greetings(
	update Update,
) Greetings {
	return func(name any) Spec {
		return Specs{
			T("Hello, %s!", name),
			OnClick(func() {
				// when clicked, update the name argument to upper case
				// use reflect to support all string-typed arguments
				nameValue := reflect.New(reflect.TypeOf(name))
				nameValue.Elem().SetString(
					strings.ToUpper(fmt.Sprintf("%s", name)))
				update(nameValue.Interface())
			}),
		}
	}
}

// string-typed states
type (
	TheWorld string
	TheDomUI string
)

// define two types in one function
func (_ Def) Names() (TheWorld, TheDomUI) {
	return "world", "DomUI"
}

func (_ Def) RootElement(
	greetings Greetings,
	world TheWorld,
	domUI TheDomUI,
) domui.RootElement {
	return Div(
		// use Greetings
		Div(
			greetings(world),
		),
		Div(
			greetings(domUI),
		),
	)
}

func main() {
	domui.NewApp(
		js.Global().Get("document").Call("getElementById", "app"),
		domui.Methods(new(Def))...,
	)
	time.Sleep(time.Hour * 24 * 365 * 100)
}

When updating a definition that has no dependency, instead of passing a function, a pointer can be used.

newGreetings := Greetings("Hello!")
update(&newGreetings)
// is the same to
update(func() Greetings {
  return "Hello!"
})

To do something on App inits, define one or more OnAppInit

func (_ Def) InitFoo() domui.OnAppInit {
  // do something
}

func (_ Def) InitBar() domui.OnAppInit {
  // do something
}

To access the DOM element in an event handler, use a js.Value parameter

type Foo Spec

func (_ Def) Foo() Foo {
  retrun Div(
    OnClick(func(elem js.Value) {
      _ = elem.Call("getAttribute", "href").String()
    }),
  )
}

Specs can be cached for reusing

type Article func(title string, content string) Spec

func (_ Def) Article() Article {
  m := domui.NewSpecMap()
  return m(
    // key
    [2]any{title, content},
    // value
    func() Spec {
      return Div( /* ... */ )
    },
  )
}

Some conditional Spec constructors are provided

var (
  A = domui.Tag("a")
  Ahref = domui.Attr("href")
  Sfont_weight = domui.Style("font-weight")
)

type Link func(href string, bold bool) Spec

func (_ Def) Link() Link {
  return func(href string, bold bool) Spec {
    return A(
      // If
      domui.If(href != "", Ahref(href)),
      // Alt
      domui.Alt(bold,
        Sfont_weight("bold"),
        Sfont_weight("normal"),
      ),
    )
  }
}

And loop constructors

type List func(elems []string) Spec

func (_ Def) List() List {
  return func(elems []string) Spec {
    return Div(
      // For
      domui.For(elems, func(s string) Spec {
        return Div(T("%s", s))
      }),
      // Range
      domui.Range(elems, func(i int, s string) Spec {
        return Div(T("%d: %s", i, s))
      }),
    )
  }
}

In react, UI components are declared as classes or functions, and there may be multiple instances of the same class. All component instances form the component tree. In addition to the component tree, a state tree or other state management scheme may be used to hold non-UI data.

In DomUI, all UI and non-UI components are defined as type/value pairs. UI components are just Spec types or function types that returning Spec. This unification greatly reduces the complexity of nearly everything compared to other MVVM frameworks.

domui's People

Contributors

reusee avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.