-
Notifications
You must be signed in to change notification settings - Fork 5
/
app.go
132 lines (105 loc) · 2.34 KB
/
app.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package domui
import (
"reflect"
"syscall/js"
"time"
"github.com/reusee/dscope"
)
type RootElement Spec
type App struct {
wrapElement js.Value
element js.Value
getScope dscope.GetScope
mutate dscope.Mutate
dirty chan struct{}
rootNode *Node
fns chan any
}
func NewApp(
renderElement js.Value,
defs ...any,
) *App {
app := &App{
dirty: make(chan struct{}, 1),
fns: make(chan any),
}
scope := dscope.NewMutable(
func() Update {
return app.Update
},
func() *App {
return app
},
)
scope.Assign(&app.getScope, &app.mutate)
defs = append(defs, dscope.Methods(new(Def))...)
app.mutate(defs...)
var onInit OnAppInit
app.getScope().Assign(&onInit)
onInit()
parentElement := js.Value(renderElement)
parentElement.Set("innerHTML", "")
wrap := document.Call("createElement", "div")
parentElement.Call("appendChild", wrap)
element := document.Call("createElement", "div")
wrap.Call("appendChild", element)
app.wrapElement = wrap
app.element = element
go func() {
for {
select {
case <-app.dirty:
app.Render()
case fn := <-app.fns:
app.getScope().Call(fn)
}
}
}()
app.Render()
return app
}
type OnAppInit func()
var _ dscope.Reducer = OnAppInit(nil)
func (_ OnAppInit) Reduce(_ Scope, vs []reflect.Value) reflect.Value {
return dscope.Reduce(vs)
}
func (_ Def) OnAppInit() OnAppInit {
return func() {}
}
func (a *App) Update(decls ...any) Scope {
scope := a.mutate(decls...)
select {
case a.dirty <- struct{}{}:
default:
}
return scope
}
var rootElementType = reflect.TypeOf((*RootElement)(nil)).Elem()
type SlowRenderThreshold time.Duration
func (_ Def) SlowRenderThreshold() SlowRenderThreshold {
return SlowRenderThreshold(time.Millisecond) * 50
}
func (a *App) Render() {
t0 := time.Now()
var slowThreshold SlowRenderThreshold
defer func() {
e := time.Since(t0)
if e > time.Duration(slowThreshold) {
log("slow render in %v", time.Since(t0))
}
}()
scope := a.getScope()
var rootElement RootElement
scope.Assign(&slowThreshold, &rootElement)
newNode := rootElement.(*Node)
var err error
a.element, err = patch(scope, newNode, a.element, a.rootNode)
ce(err)
a.rootNode = newNode
}
func (a *App) HTML() string {
if a.element.InstanceOf(htmlElement) {
return a.element.Get("outerHTML").String()
}
return a.element.Get("nodeValue").String()
}